Почему `переменный шаблон` в C ++ не ведет себя должным образом? - PullRequest
0 голосов
/ 16 декабря 2018
#include <type_traits>

template<typename T>
struct remove_cvref
{
    using type = std::remove_cv_t<
            std::remove_reference_t<T>>;
};

template<typename T>
using remove_cvref_t = 
typename remove_cvref<T>::type;

template<typename T>
constexpr bool isCc = std::is_copy_constructible_v<
remove_cvref_t<T>>;

class A final
{
public:
    A() = default;
    template<typename T, bool = isCc<T>> // error
    A(T&&) {}
};

A f()
{
    A a;
    return a;
}

int main()
{}

Сообщение об ошибке:

error : constexpr variable 'isCc<const A &>' must be initialized by a constant expression
1>main.cpp(14):  note: in instantiation of variable template specialization 'isCc<const A &>' requested here
1>main.cpp(15):  note: in instantiation of default argument for 'A<const A &>' required here
1>C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\type_traits(847):  note: while substituting deduced template arguments into function template 'A' [with T = const A &, b1 = (no value)]
1>main.cpp(8):  note: in instantiation of variable template specialization 'std::is_copy_constructible_v<A>' requested here
1>main.cpp(14):  note: in instantiation of variable template specialization 'isCc<A>' requested here
1>main.cpp(15):  note: in instantiation of default argument for 'A<A>' required here
1>main.cpp(21):  note: while substituting deduced template arguments into function template 'A' [with T = A, b1 = (no value)]

Однако, если я изменю класс A следующим образом:

class A final
{
public:
    A() = default;
    template<typename T, 
    bool = std::is_copy_constructible_v<
        remove_cvref_t<T>>> // ok
    A(T&&) {}
};

Тогда все в порядке.

Почему C ++ variable template ведет себя не так, как ожидалось?

Ответы [ 3 ]

0 голосов
/ 16 декабря 2018

Я успешно скомпилировал исходный предложенный код OP's в Visual Studio 2017 CE версии 15.8.6 с языковым стандартом моего компилятора, установленным на ISO C++ Latest Draft Standard (/std:c++latest) в моих настройках IDE's, и моя машина работает под управлением Windows 7 64bit Ultimate.Я собрал и отредактировал код в режиме Debug - x86.

Я даже зашел так далеко и сделал вызов его функции f() в main, и он все еще собирался, компилировался, работал и выходил без ошибок.

Затем он ответил в комментариях:

Мой компилятор - Clang 7.0 на Windows

Я не знаю, является ли это ошибкойв Clang's компиляторе или, если Clang просто интерпретирует его по-разному.

Возможно, попробуйте скомпилировать исходную попытку с другими компиляторами, если можете, попробуйте GCC или другую версию Clang и посмотритеесли вы получите другие результаты.

Это что-то интересное, и я считаю, что нужно провести дополнительное расследование, чтобы определить, связано ли оно конкретно с Clang's компилятором.

0 голосов
/ 16 декабря 2018

В момент, когда создается экземпляр std::is_copy_constructible_v<A>, т. Е. Сразу после определения isCc, A не завершено, в то время как std::is_copy_constructible_v требует завершения аргумента шаблона.

Вопрос о том, должен ли этот код работать, остается вопросом разработки: Базовая языковая проблема 287 , поэтому разумно, чтобы некоторые компиляторы принимали ваш код, а другие отклоняли его.

Вверсия без isCc, даже в момент создания экземпляра std::is_copy_constructible_v<A>, A завершена 1 , поэтому все компиляторы с радостью принимают код.


1 Соответствующие правила в стандарте:

[class.member] / 6

A полный контекст класса класса - это

  • тело функции,
  • аргумент по умолчанию,
  • noexcept-спецификатор ([кроме.spec]),
  • контрактусловие или
  • инициализатор элемента по умолчанию

в пределах спецификации элемента класса ...

[class.member] / 7

... Класс считается завершенным в контексте полного класса ...

0 голосов
/ 16 декабря 2018

is_copy_constructible_v добавлено в C ++ 17, а std::remove_cvref добавлено в C ++ 20.Поддержка C ++ 20 все еще экспериментальная.

Написание правильного кода C ++ 14 решит проблему:

template<typename T>
constexpr bool isCc = std::is_copy_constructible<std::remove_reference_t<::std::remove_cv_t<T>>>::value;

или C ++ 17:

template<typename T>
constexpr bool isCc = std::is_copy_constructible_v<std::remove_reference_t<::std::remove_cv_t<T>>>;

Ответ на следующий вопрос:

std::is_copy_constructible Требования к параметрам шаблона:

T должен иметь полный тип, cv void или массив с неизвестной границей.

, что не относится к template<typename T, bool = isCc<T>>, когда T равно A

...