为什么is_constructible声称某些东西是可构造的,当它不是?

时间:2013-02-11 16:06:45

标签: c++ templates c++11 constructor typetraits

以下程序在使用GCC 4.7和clang 3.2编译时,会产生“1”作为输出。

#include <type_traits>

struct foo {
    template<typename T>
    foo(T) {
        static_assert(not std::is_same<int, T>(), "no ints please");
    }
};

#include <iostream>    
int main() {
    std::cout << std::is_constructible<foo, int>();
}

这令人困惑。 foo int显然无法构建main!如果我将int main() { foo(0); } 更改为以下内容,则由于静态断言失败,两个编译器都会拒绝它:

{{1}}

两个编译器怎么说它是可构造的?

2 个答案:

答案 0 :(得分:22)

这是标准所说的(§20.9.5/ 6),我强调:

  

给出以下函数原型:

template <class T>
typename add_rvalue_reference<T>::type create();
     

模板特化的谓词条件   当且仅当,is_constructible<T, Args...>才会得到满足   对于一些发明的人来说,下面的变量定义会很好   变量t

T t(create<Args>()...);
     

[注意:这些令牌永远不会被解释为函数   宣言。 -end note ]

     

执行访问检查,就好像在与T和。无关的上下文中一样   任何Args只有当前上下文的有效性   考虑变量初始化。 [注意:评估   初始化可能导致副作用,例如实例化   类模板特化和函数模板特化,   生成隐式定义的函数,等等。这样的一面   效果不在“直接上下文”中,并且可能导致   程序不正确。 -end note ]

断言仅在实例化模板构造函数时失败。但是,正如在注释中清除的那样,该断言不在所考虑的变量定义的直接上下文中,因此不会影响其“有效性”。因此,编译器可以将该定义计为有效,从而声称foo确实可以从int构建,即使实际尝试从foo构造int也会导致一个不正确的计划。

请注意,编译器也被允许,而不是让is_constructible产生错误,只是根据断言拒绝原始程序,即使两者都没有。

答案 1 :(得分:5)

foo2是您的foofoo1是一个foo,可以满足您foo的要求。

#include <type_traits>
#include <utility>

struct foo1 {
  template<typename T,typename=typename std::enable_if< !std::is_same<int, T>::value >::type>
  foo1(T) {
    static_assert(not std::is_same<int, T>(), "no ints please");
  }
};
struct foo2 {
  template<typename T>
  foo2(T) {
    static_assert(not std::is_same<int, T>(), "no ints please");
  }
};

#include <iostream>    
int main() {
  std::cout << std::is_constructible<foo1, int>();
  std::cout << std::is_constructible<foo2, int>();
}