如何检查类型是否具有 constexpr 构造函数

How to check a type has constexpr constructor

我希望我的 class 对没有 constexpr 构造函数的类型使用另一个实现。

像这样:

template <typename A>
class foo
{
public:

    // if A has constexpr constructor
    constexpr foo() :_flag(true) { _data._a = A(); }
    // else
    constexpr foo() : _flag(false) { _data.x = 0; }

    ~foo(){}

    bool _flag;
    union _data_t
    {
        _data_t() {} // nothing, because it's just an example
        ~_data_t() {}
        A _a;
        int x;
    }_data;
};

为了实现标题所说的,我尝试这样做:

template<typename _t, _t = _t()>
constexpr bool f()
{
    return true;
}
template<typename _t>
constexpr bool f()
{
    return false;
}

它适用于没有 constexpr 构造函数的类型。 但对于其他类型,它会导致编译错误和不明确的重载。

那我怎么检查呢?

我想您可以将 SFINAE 与逗号运算符的强大功能结合使用

按照你的想法,你可以重写你的f()函数如下

template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }

template <typename>
constexpr bool f (long)
{ return false; }

注意技巧:int = (T{}, 0) 第二个模板参数

仅当 T{} 可以构造为 constexpr(因为 (T{}, 0) 是模板参数的参数)时,才启用 f()(逗号运算符的强大功能),否则 SFINAE擦除 f().

的第一个版本

并观察 f() 的第一个版本收到未使用的 int,而第二个版本收到 long。这样,第一个版本是首选,如果可用,用 int 调用 f();选择第二个,总比没有好,当第一个不可用时(当第一个模板参数不是 constexpr 默认构造时)。

现在您可以为 foo 构造两个模板构造函数,您可以根据模板参数 T(默认为 A)是或不是的事实来选择 enable/disable 't constexpr 构造

template <typename T = A,
          std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "constexpr" << std::endl; }

template <typename T = A,
          std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
constexpr foo() { std::cout << "not constexpr" << std::endl; }

以下是一个完整的编译示例(C++14 或更新版本,但您可以针对 C++11 进行修改):

#include <iostream>
#include <type_traits>

template <typename T, int = (T{}, 0)>
constexpr bool f (int)
{ return true; }

template <typename>
constexpr bool f (long)
{ return false; }

template <typename A>
struct foo
{
  template <typename T = A,
            std::enable_if_t<f<T>(0), std::nullptr_t> = nullptr>
  constexpr foo() { std::cout << "constexpr" << std::endl; }

  template <typename T = A,
            std::enable_if_t<not f<T>(0), std::nullptr_t> = nullptr>
  constexpr foo() { std::cout << "not constexpr" << std::endl; }
};


struct X1 { constexpr X1 () {} };
struct X2 {           X2 () {} };


int main()
{
  foo<X1> f1;  // print "constexpr"
  foo<X2> f2;  // print "not constexpr"
}