Я хочу, чтобы следующее компилировалось:
#include <type_traits>
template<typename T>
class C {
public:
C() {}
C( const C &that ) {}
};
void foo( const C<const char> &c );
int main() {
C<const int> i;
foo( C<char>{} );
}
Конечно, это не так, поскольку foo
ожидает C<const char>
, и я передаю C<char>
. Поэтому я хочу добавить неявное преобразование из C<T>
в C<const T>
. В первую очередь мы добавляем следующий конструктор к C
:
C( const C< std::remove_const_t<T> > &that ) {}
Это не работает, потому что если T
само по себе не является константой, это определение идентично конструктору копирования, который означает, что мы переопределяем один и тот же конструктор дважды:
so.cpp: In instantiation of ‘class C<char>’:
so.cpp:17:18: required from here
so.cpp:9:5: error: ‘C<T>::C(const C<typename std::remove_const<_Tp>::type>&) [with T = char; typename std::remove_const<_Tp>::type = char]’ cannot be overloaded with ‘C<T>::C(const C<T>&) [with T = char]’
C( const C< std::remove_const_t<T> > &that ) {}
^
so.cpp:7:5: note: previous declaration ‘C<T>::C(const C<T>&) [with T = char]’
C( const C &that ) {}
Вторая попытка, попробуйте использовать enable_if
:
template< std::enable_if_t< std::is_const_v<T>, int > = 0 >
C( const C< std::remove_const_t<T> > &that ) {}
, который не работает. Поскольку шаблон не зависит от какого-либо вывода, сделанного в аргументах конструктора, он оценивается слишком рано:
so.cpp: In instantiation of ‘class C<char>’:
so.cpp:18:18: required from here
so.cpp:10:5: error: no type named ‘type’ in ‘struct std::enable_if<false, int>’
C( const C< std::remove_const_t<T> > &that ) {}
^
Давайте искусственно заставим его зависеть от аргументов, тогда:
template<
typename V,
std::enable_if_t<
std::is_const_v<T> && std::is_same_v<T, V>,
int
> = 0
>
C( const C< std::remove_const_t<V> > &that ) {}
Я не уверен почему это не удается. Я получаю следующую ошибку:
so.cpp: In function ‘int main()’:
so.cpp:24:10: error: invalid initialization of reference of type ‘const C<const char>&’ from expression of type ‘C<char>’
foo( C<char>{} );
^~~~~~~~~
so.cpp:19:6: note: in passing argument 1 of ‘void foo(const C<const char>&)’
void foo( const C<const char> &c );
^~~
Я подозреваю, что дедукция происходит слишком глубоко, чтобы компилятор мог понять, какую подстановку ему нужно сделать.
Как мне решить эту проблему?
Ps Да, я знаю, что в этом случае Я могу использовать неявный конструктор копирования.