“C ++编程语言”(第四版)。第28.4章(第796页)解释了enable_if,并给出了使operator->()的条件定义的示例。本书中的示例只是一个代码片段,我按如下方式将其完成了一个程序:
#include <iostream>
#include <type_traits>
#include <complex>
using namespace std;
template<bool B, typename T>
using Enable_if=typename std::enable_if<B,T>::type;
template<typename T>
constexpr bool Is_class(){ //the book example misses constexpr
return std::is_class<T>::value;
}
template<typename T>
class Smart_pointer{
public:
Smart_pointer(T* p):data(p){
}
T& operator*();
Enable_if<Is_class<T>(),T>* operator->(){
return data;
}
~Smart_pointer(){
delete data;
}
private:
T* data;
};
int main()
{
Smart_pointer<double> p(new double); //compiling error in g++ 4.7.2: no type named 'type' in 'struct std::enable_if<false, double>'
//Smart_pointer<std::complex<double>> q(new std::complex<double>);//compile successfully.
}
上面的代码不能在gcc 4.7.2中编译。编译器抱怨:错误:'struct std :: enable_if'
中没有名为'type'的类型根据书中的解释,如果T不是类,则将忽略operator-&gt;()。但是,这并不能解释编译错误。编译错误表示相反,即使T不是类,也不会忽略operator-&gt;()的定义。这篇文章std::enable_if to conditionally compile a member function似乎解释了编译错误。但这篇文章似乎与书的解释不一致。任何人都可以帮助和解释成员函数的enable_if的用法?在上面的例子中,如果我只想在T为class时定义operator-&gt;(),是否有一个干净而优雅的解决方案?谢谢。
非常感谢您的回复。还有另一种基于重载的解决方法(我不会说它比其他发布的解决方案更好)
template<typename T>
T* Smart_pointer<T>::operator->(){
op_arrow(std::integral_constant<bool,Is_class<T>()>());
}
private:
T* op_arrow(std::true_type){
return data;
}
T* op_arrow(std::false_type);
答案 0 :(得分:2)
首先,正如@jrok所提到的以及您链接到的帖子:
你必须有一个模板函数才能使用enable_if
。但是在你的特定情况下这不是一个好主意,因为没有理由这样做operator->
模板化。而且,它不会起作用!因为没有办法实例化特定的运营商 - &gt;()!它没有(也不能)任何参数,当你在某个对象上调用时,没有语法来指定它!因此,T
(或whateve虚拟类型)将/不能从此调用中推断出来。
因此,作为一种解决方法,您可以使用编译时条件继承。即像这样的smth:
template <typename T, bool IsClass>
struct smart_base
{
struct base {};
};
template <typename T>
struct smart_base<T, true>
{
struct base
{
T* operator->()
{
// do that ever you wanted to do
}
};
};
template <typename T>
struct smart : public smart_base<T, std::is_class<T>::value>::base
{
// depending on T here you have, or have no operator-> inherited
};
你必须明白,在你的基类中实际拥有operator->
将需要将一些函数和/或数据成员移动到基础类:)或者你可以使用CRTP技术来访问来自基础的派生类的成员:)
答案 1 :(得分:1)
你可以在C ++ 11中约束operator->
,但它需要一个相当尴尬的技巧。您必须使该成员成为模板,并且您必须使Enable_if
依赖于模板参数,以便在实例化成员时发生SFINAE,而不是在实例化类时发生简单错误(Live at Coliru ):
template<typename T>
class Smart_pointer {
public:
...
template <class U=T,class=Enable_if<Is_class<U>(),void>>
T* operator->() const { return data; }
};
那就是说,我不知道这样做是否真的是个好主意。 The error message when used with an inappropriate T
:
main.cpp:35:4: error: base operand of ‘->’ has non-pointer type ‘Smart_pointer<double>’
p->foo();
^
与what you get if you just declare operator->
unconditionally相比,并没有那么多改进:
template<typename T>
class Smart_pointer {
public:
...
T* operator->() const { return data; }
};
main.cpp:35:6: error: request for member ‘foo’ in ‘* p.Smart_pointer<T>::operator-><double>()’, which is of non-class type ‘double’
p->foo();
^
并且每个阅读过您代码的人都不必要求您解释operator->
到底发生了什么。