当前位置: 首页 > 知识库问答 >
问题:

用SFINAE检测constexpr

周飞
2023-03-14

我正在努力升级一些C代码,以利用C 11中的新功能。我有一个trait类,其中有几个函数返回基本类型,大多数情况下,但并非总是返回常量表达式。我想根据函数是否为constexpr来做不同的事情。我提出了以下方法:

template<typename Trait>
struct test
{
    template<int Value = Trait::f()>
    static std::true_type do_call(int){ return std::true_type(); }

    static std::false_type do_call(...){ return std::false_type(); }

    static bool call(){ return do_call(0); }
};

struct trait
{
    static int f(){ return 15; }
};

struct ctrait
{
    static constexpr int f(){ return 20; }
};

int main()
{
   std::cout << "regular: " << test<trait>::call() << std::endl;
   std::cout << "constexpr: " << test<ctrait>::call() << std::endl;
}

额外的< code>int/...参数的作用是,如果SFINAE之后两个函数都可用,重载解析将选择第一个函数。

用Clang 3.2编译并运行该代码显示:

regular: 0
constexpr: 1

所以这似乎有效,但我想知道代码是否合法C 11。特别是因为我的理解是SFINAE的规则已经改变。

共有2个答案

公沈浪
2023-03-14

在@marshall-clow的提示下,我整理了一个更通用的类型特征版本,用于检测constexpr。我根据 std::invoke_result 对其进行建模,但由于 constexpr 依赖于输入,因此模板参数用于传入的值,而不是类型。

它有一定的局限性,因为模板args只能是一组有限的类型,当它们到达方法调用时,它们都是const。如果您需要其他类型或引用参数的非const左值,您可以轻松测试const r包装器方法。

因此,与其说是实际有用的代码,不如说是一种练习和演示。

以及模板的使用

namespace constexpr_traits {

namespace detail {

// Call the provided method with the provided args.
// This gives us a non-type template parameter for void-returning F.
// This wouldn't be needed if "auto = F(Args...)" was a valid template
// parameter for void-returning F.
template<auto F, auto... Args>
constexpr void* constexpr_caller() {
    F(Args...);
    return nullptr;
}

// Takes a parameter with elipsis conversion, so will never be selected
// when another viable overload is present
template<auto F, auto... Args>
constexpr bool is_constexpr(...) { return false; }

// Fails substitution if constexpr_caller<F, Args...>() can't be
// called in constexpr context
template<auto F, auto... Args, auto = constexpr_caller<F, Args...>()>
constexpr bool is_constexpr(int) { return true; }

}

template<auto F, auto... Args>
struct invoke_constexpr : std::bool_constant<detail::is_constexpr<F, Args...>(0)> {};

}

wandbox上使用案例的现场演示

陶修洁
2023-03-14

注意:我在这里打开了一个关于OPs代码是否真正有效的问题。我下面重写的示例在任何情况下都可以工作。

但是我想知道代码是否是合法的C 11

确实如此,尽管默认模板参数可能被认为有点不寻常。我个人更喜欢以下风格,这类似于您(阅读:我)编写特征来检查函数是否存在的方式,只是使用非类型模板参数并省略 decltype

#include <type_traits>

namespace detail{
template<int> struct sfinae_true : std::true_type{};
template<class T>
sfinae_true<(T::f(), 0)> check(int);
template<class>
std::false_type check(...);
} // detail::

template<class T>
struct has_constexpr_f : decltype(detail::check<T>(0)){};

活生生的例子。

解释时间~

您的原始代码可以工作,因为默认模板参数的实例化点是其函数模板的实例化点,在您的情况下,这意味着在< code>main中,因此它不能在此之前被替换。

§14.6.4.1【温度点】p2

如果函数模板[...]的调用方式使用了该函数模板的默认参数的定义[...],默认参数的实例化点就是函数模板的实例化点[...].

在那之后,这只是通常的SFINAE规则。

至少我是这样认为的,在标准中并不完全清楚。

 类似资料:
  • 我有以下特征来检测两种类型是否可除,并返回结果操作类型或另一种type,否则返回: 当我尝试使用不可分割的类型时,这对libstdc很有效,但对libc不起作用,例如: 我得到以下错误: /Library/Developer/CommandLineTools/usr/include/c/v1/chrono:764:81:错误:std:_1::common_type“”typename common

  • Stroustrup先生尚未完成这个主题,请稍后再来。

  • 这是用SFINAE检测constepr的后续问题。 我想检测元组的一个元素(或任何可以与一起使用的元素)是否为constexpr。所以我写了以下与Xeo提供的类似的帮助: 现在,我的测试驱动程序代码: 然而,这总是为我打印出!为了检查是否由于某些原因没有总是调用假重载,我注释掉了假重载并得到了编译器错误: 注意:已忽略候选模板:替换失败[N=0,T=const std::tuple]:非类型模板参

  • 考虑下面这个函数模板: template <typename T, typename U> void foo(T t, typename U::type u) { // ... } 到本节为止,我们所有的例子都保证了一旦咱们敲定了模板参数中 T 和 U,函数参变量 t 和 u 的类型都是成立的,比如下面这样: struct X { typedef float type; }; templ

  • 泛型lambdas能利用“替换失败不是错误”规则吗?例子 是否有任何解决方法或计划将其包含在语言中?此外,由于通用lambda是引擎盖下的模板化函数对象,因此无法做到这一点不是有点奇怪吗?

  • 当是基本类型时,我试图使用SFINAE重载以返回一个副本,当是一个类时,我试图重载一个常量引用。 在我下面的示例中,当使用时,我无法删除第二个重载(使用)。 也就是说,我得到的错误是: 我做错了什么?