模板中构造函数的条件定义 class
Conditional definition of constructor in template class
我要编译以下内容:
#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
本身不是 const,这个定义就等同于复制构造函数,这意味着我们重新定义了同一个构造函数两次:
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 );
^~~
我怀疑推导发生得太深以至于编译器无法理解它需要进行什么替换。
我该如何解决这个问题?
P.s。
是的,我知道在这种情况下我可以使用隐式复制构造函数。
您可以在函数上使用 SFINAE,但函数应该是模板,
和条件应该取决于该模板,请记住 class 中的 T
对于成员函数是固定的:
template<typename T>
class C {
public:
C() {}
C( const C &that ) {}
template <typename U,
std::enable_if_t<std::is_same_v<U, std::remove_const_t<T>>, int> = 0>
C( const C<U> &that ) {}
};
在 C++20 中会有很好的语法:
template<typename T>
class C {
public:
C() {}
C( const C &that ) {}
C( const C< std::remove_const_t<T> > &that ) requires(std::is_const<T>::value) {}
};
对于您的代码:
template< std::enable_if_t< std::is_const_v<T>, int > = 0 >
C( const C< std::remove_const_t<T> > &that ) {}
不是替换上下文,参数由 class 固定,您有硬错误而不是预期的 SFINAE。
对于
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 ) {}
你可能有 SFINAE,但是 V
是不可推导的,所以你必须提供它,但不能在调用站点提供它,因为它是一个构造函数。
简单的修复是给出默认值:
template<
typename V = T,
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 ) {}
(因为 V 是必要的 T,您可以删除 std::is_same_v<T, V>
但将 std::is_const_v<T>
更改为 std::is_const_v<V>
以获得 SFINAE)
我要编译以下内容:
#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
本身不是 const,这个定义就等同于复制构造函数,这意味着我们重新定义了同一个构造函数两次:
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 );
^~~
我怀疑推导发生得太深以至于编译器无法理解它需要进行什么替换。
我该如何解决这个问题?
P.s。 是的,我知道在这种情况下我可以使用隐式复制构造函数。
您可以在函数上使用 SFINAE,但函数应该是模板,
和条件应该取决于该模板,请记住 class 中的 T
对于成员函数是固定的:
template<typename T>
class C {
public:
C() {}
C( const C &that ) {}
template <typename U,
std::enable_if_t<std::is_same_v<U, std::remove_const_t<T>>, int> = 0>
C( const C<U> &that ) {}
};
在 C++20 中会有很好的语法:
template<typename T>
class C {
public:
C() {}
C( const C &that ) {}
C( const C< std::remove_const_t<T> > &that ) requires(std::is_const<T>::value) {}
};
对于您的代码:
template< std::enable_if_t< std::is_const_v<T>, int > = 0 >
C( const C< std::remove_const_t<T> > &that ) {}
不是替换上下文,参数由 class 固定,您有硬错误而不是预期的 SFINAE。
对于
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 ) {}
你可能有 SFINAE,但是 V
是不可推导的,所以你必须提供它,但不能在调用站点提供它,因为它是一个构造函数。
简单的修复是给出默认值:
template<
typename V = T,
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 ) {}
(因为 V 是必要的 T,您可以删除 std::is_same_v<T, V>
但将 std::is_const_v<T>
更改为 std::is_const_v<V>
以获得 SFINAE)