Подтвердить что ты не робот

Почему is_constructible требует, чтобы что-то было конструктивным, если это не так?

Следующая программа, скомпилированная с помощью 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);
}

Почему оба компилятора говорят, что они конструктивны?

4b9b3361

Ответ 1

Это то, что стандарт должен сказать (§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, даже если на самом деле попытка построить a foo из int приводит к плохо сформированной программе.

Обратите внимание, что компиляторам также разрешено вместо is_constructible yield false просто отклонить исходную программу на основе этого утверждения, даже если это не так.

Ответ 2

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>();
}