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

R. *_*des 14 c++ templates constructor type-traits c++11

当使用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>();
}
Run Code Online (Sandbox Code Playgroud)

这令人困惑.foo显然不是可以构建的int!如果我改为main以下,由于静态断言失败,两个编译器都拒绝它:

int main() {
    foo(0);
}
Run Code Online (Sandbox Code Playgroud)

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

R. *_*des 22

这就是标准所说的(§20.9.5/ 6),我的重点是:

给出以下函数原型:

template <class T>
typename add_rvalue_reference<T>::type create();
Run Code Online (Sandbox Code Playgroud)

is_constructible<T, Args...>当且仅当以下变量定义适用于某些发明变量时,才应满足模板特化的谓词条件 t:

T t(create<Args>()...);
Run Code Online (Sandbox Code Playgroud)

[ 注意:这些标记永远不会被解释为函数声明.- 尾注 ]

执行访问检查就好像在与其无关的上下文T中执行Args.仅考虑变量初始化的直接上下文的有效性.[ 注意:初始化的评估可能会导致副作用,例如类模板特化和函数模板特化的实例化,隐式定义函数的生成等等.这种副作用不在"直接背景"中,并且可能导致程序形成不良.- 尾注 ]

断言仅在实例化模板构造函数时失败.但是,正如在注释中清除的那样,该断言不在所考虑的变量定义的直接上下文中,因此不会影响其"有效性".因此编译器可以将该定义视为有效,因此声称它foo确实是可构造的int,即使实际上是试图fooint一个不正确的程序中构造一个结果.

请注意,编译器也被允许,而不是is_constructible产生错误,只是基于断言拒绝原始程序,即使没有人这样做.

  • 请注意,它才有意义.`std :: is_constructible <>`确定是否有一个构造函数接受参数,而不是构造函数是否定义良好.考虑到在处理`is_constructible`时编译器的定义(以及`static_assert`)不需要对编译器可见.如果你想要这样做,请使用SFINAE来禁用`int`的构造函数,或者添加`foo(int)= delete;`这将标记构造函数不可用(不是100%肯定这最后一种方法,但是它应该工作) (11认同)

Yak*_*ont 5

foo2是你的foo. foo1是一个做你想做的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>();
}
Run Code Online (Sandbox Code Playgroud)