【问题标题】:usage of enable_if for non-templated member function [duplicate]对非模板化成员函数使用 enable_if [重复]
【发布时间】:2013-08-07 23:17:21
【问题描述】:

C++ 编程语言(第四版)一书。第 28.4 章(第 796 页)解释了 enable_if 并给出了使 operator->() 有条件的定义的示例。书中的例子只是一个代码sn-p,我完成成一个程序如下:

#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 中编译。编译器报错:error: no type named 'type' in 'struct std::enable_if'

根据书中的解释,如果 T 不是类,operator->() 将被忽略。但是,这并不能解释编译错误。编译错误表明相反,即使 T 不是类,也不会忽略 operator->() 的定义。这篇文章std::enable_if to conditionally compile a member function 似乎解释了编译错误。但该帖子似乎与书中的解释不一致。任何人都可以帮助解释成员函数的 enable_if 的用法吗?在上面的例子中,如果我只想在 T 是类时定义 operator->(),有没有一个干净优雅的解决方案?谢谢你。

非常感谢您的回复。还有另一种基于重载的解决方法(我不会说它比其他发布的解决方案更好)

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);

【问题讨论】:

  • 您链接到的帖子很好地解释了它 - 这仅在函数本身是模板时才有效。

标签: c++ enable-if


【解决方案1】:

首先,正如@jrok 和您链接到的帖子所提到的: 你必须有一个模板函数才能使用enable_if。但在您的特定情况下这不是一个好主意,因为没有理由这样做operator-&gt; 模板化。而且,这行不通!因为没有办法实例化一个特定的操作符->()!它没有(也不能)任何参数,并且当您在某个对象上调用它时,也没有语法来指定它!所以,T(或任何虚拟类型)将/不能从这个调用中推断出来。

因此,作为一种解决方法,您可以使用编译时条件继承。即这样的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-&gt; 将需要将一些函数和/或数据成员移动到基类:) 或者您可以使用 CRTP 技术来访问从基类派生的类:)

【讨论】:

  • +1,虽然,static_assert 会更容易做到。您也可以让 operator-> 将所有工作转发给其他一些私有模板函数并在那里执行 SFINAE。
【解决方案2】:

您可以在 C++11 中约束 operator-&gt;,但这需要一个相当笨拙的技巧。您必须使成员成为模板,并且必须使 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-&gt; 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-&gt;到底发生了什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-22
    • 2012-10-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多