为 void 类型禁用 class 模板成员?
Disable class template member for void types?
考虑以下基本 class 模板:
#include <type_traits>
template < typename T >
class A {
public:
A() = default;
T obj;
template < typename U = T, typename = typename std::enable_if< !std::is_void< U >::value >::type >
T& get();
};
我正在使用 <type_traits>
来获得一个简单的 SFINAE 实现,如果模板参数为空,它会隐藏 get()
。
但是,我仍然收到 void 类型的编译器错误 error: forming reference to void
,我不确定原因是什么。 class 声明语法有什么问题吗?
int main(int argc, char const *argv[]) {
A<int> b;
A<void> t; // error: forming reference to void
return 0;
}
编辑:有人指出问题是 obj
不能为空。一种解决方法是改用指针:
#include <type_traits>
#include <memory>
template < typename T >
class A {
public:
A() = default;
std::shared_ptr< T > obj;
template < typename U = T, typename = typename std::enable_if< !std::is_void< U >::value >::type >
U& get() { return *obj; };
};
但我仍然得到 error: forming reference to void
,这意味着编译器仍在尝试编译 get()
。
第一个问题是您试图使用
定义一个变量
T obj;
其中 T 是 void
。但是根据documentation
Any of the following contexts requires type T to be complete:
definition of an object of type T;
但由于 void
是 不完整类型 ,您得到错误:
error: 'A::obj' has incomplete type
第二个 我们也不能有 return 类型的 void&
这就是为什么你会得到你提到的错误:
error: forming reference to void
您可以解决问题:
#include <type_traits>
#include <iostream>
template < typename T >
class A {
public:
A() = default;
T *obj = nullptr;//note a pointer
template < typename U = T>
typename std::enable_if<!std::is_same<U, void>::value,U&>::type get()
{
std::cout <<"called"<<std::endl;
if(obj != nullptr)
{
std::cout<<"not null"<<std::endl;
return *obj;
}
else
{
std::cout<<"null"<<std::endl;
obj = new T{};
return *obj;
}
}
~A()
{
std::cout<<"destructor called"<<std::endl;
if(!std::is_same<T, void>::value)
{
std::cout<<"deleted"<<std::endl;
delete obj;
obj = nullptr;
}
else
{
std::cout<<"not deleted"<<std::endl;
}
}
};
int main(int argc, char const *argv[]) {
A<int> b;
std::cout<< b.get() <<std::endl;//this will print the string "called" and the integer 0 on console
std::cout<<"------------------"<<std::endl;
int &p = b.get();
std::cout<<"------------------"<<std::endl;
p = 32;
std::cout << b.get()<<std::endl;
std::cout<<"------------------"<<std::endl;
A<void> t;
return 0;
}
此外,不要忘记在析构函数中使用 delete
。
解决这个问题的一个简单方法(甚至与 C++98 兼容)是编写 A
的特化。它很大 class,但它可能很烦人。
另一种方法是强制 obj
是 void
以外的其他类型。
#include <type_traits>
template < typename T >
class A {
public:
A() = default;
struct empty{};
[[no_unique_address]]
std::conditional_t<std::is_same_v<T, void>, empty, T> obj;
template <typename U = T, typename = typename std::enable_if<!std::is_void_v<U>>::type>
U& get() { return obj; }
};
int main()
{
A<void> v;
A<int> x;
int& y = x.get();
}
考虑以下基本 class 模板:
#include <type_traits>
template < typename T >
class A {
public:
A() = default;
T obj;
template < typename U = T, typename = typename std::enable_if< !std::is_void< U >::value >::type >
T& get();
};
我正在使用 <type_traits>
来获得一个简单的 SFINAE 实现,如果模板参数为空,它会隐藏 get()
。
但是,我仍然收到 void 类型的编译器错误 error: forming reference to void
,我不确定原因是什么。 class 声明语法有什么问题吗?
int main(int argc, char const *argv[]) {
A<int> b;
A<void> t; // error: forming reference to void
return 0;
}
编辑:有人指出问题是 obj
不能为空。一种解决方法是改用指针:
#include <type_traits>
#include <memory>
template < typename T >
class A {
public:
A() = default;
std::shared_ptr< T > obj;
template < typename U = T, typename = typename std::enable_if< !std::is_void< U >::value >::type >
U& get() { return *obj; };
};
但我仍然得到 error: forming reference to void
,这意味着编译器仍在尝试编译 get()
。
第一个问题是您试图使用
定义一个变量T obj;
其中 T 是 void
。但是根据documentation
Any of the following contexts requires type T to be complete:
definition of an object of type T;
但由于 void
是 不完整类型 ,您得到错误:
error: 'A::obj' has incomplete type
第二个 我们也不能有 return 类型的 void&
这就是为什么你会得到你提到的错误:
error: forming reference to void
您可以解决问题:
#include <type_traits>
#include <iostream>
template < typename T >
class A {
public:
A() = default;
T *obj = nullptr;//note a pointer
template < typename U = T>
typename std::enable_if<!std::is_same<U, void>::value,U&>::type get()
{
std::cout <<"called"<<std::endl;
if(obj != nullptr)
{
std::cout<<"not null"<<std::endl;
return *obj;
}
else
{
std::cout<<"null"<<std::endl;
obj = new T{};
return *obj;
}
}
~A()
{
std::cout<<"destructor called"<<std::endl;
if(!std::is_same<T, void>::value)
{
std::cout<<"deleted"<<std::endl;
delete obj;
obj = nullptr;
}
else
{
std::cout<<"not deleted"<<std::endl;
}
}
};
int main(int argc, char const *argv[]) {
A<int> b;
std::cout<< b.get() <<std::endl;//this will print the string "called" and the integer 0 on console
std::cout<<"------------------"<<std::endl;
int &p = b.get();
std::cout<<"------------------"<<std::endl;
p = 32;
std::cout << b.get()<<std::endl;
std::cout<<"------------------"<<std::endl;
A<void> t;
return 0;
}
此外,不要忘记在析构函数中使用 delete
。
解决这个问题的一个简单方法(甚至与 C++98 兼容)是编写 A
的特化。它很大 class,但它可能很烦人。
另一种方法是强制 obj
是 void
以外的其他类型。
#include <type_traits>
template < typename T >
class A {
public:
A() = default;
struct empty{};
[[no_unique_address]]
std::conditional_t<std::is_same_v<T, void>, empty, T> obj;
template <typename U = T, typename = typename std::enable_if<!std::is_void_v<U>>::type>
U& get() { return obj; }
};
int main()
{
A<void> v;
A<int> x;
int& y = x.get();
}