用SFINAE检测constexpr

K-b*_*llo 35 c++ templates sfinae constexpr c++11

我正在努力升级一些C++代码以利用C++ 11中的新功能.我有一个特性类,其中有一些函数返回基本类型,这些函数大部分时间(但并不总是)返回一个常量表达式.我想根据功能是否做不同的事情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;
}
Run Code Online (Sandbox Code Playgroud)

额外的int/ ...参数是这样的,如果两个函数在SFINAE之后可用,则第一个函数通过重载分辨率来选择.

使用Clang 3.2编译和运行它显示:

regular: 0
constexpr: 1
Run Code Online (Sandbox Code Playgroud)

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

Xeo*_*Xeo 14

注意: 我在这里打开了一个关于OP代码是否实际有效的问题.我在下面重写的例子无论如何都适用.


但我想知道代码是否合法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)){};
Run Code Online (Sandbox Code Playgroud)

实例.


解释时间〜

您的原始代码有效因为默认模板参数的实例化点是其功能模板的实例化点,在您的情况下,在其中main,因此它不能在此之前替换.

§14.6.4.1 [temp.point] p2

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

在那之后,这通常是SFINAE规则.


†至少我认为如此,标准中并不完全清楚.