31

我正在尝试根据类模板参数确定调用哪个版本的成员函数。我试过这个:

#include <iostream>
#include <type_traits>

template<typename T>
struct Point
{
  void MyFunction(typename std::enable_if<std::is_same<T, int>::value, T >::type* = 0)
  {
    std::cout << "T is int." << std::endl;
  }

  void MyFunction(typename std::enable_if<!std::is_same<T, int>::value, float >::type* = 0)
  {
    std::cout << "T is not int." << std::endl;
  }
};

int main()
{
  Point<int> intPoint;
  intPoint.MyFunction();

  Point<float> floatPoint;
  floatPoint.MyFunction();
}

我认为这是说“如果 T 是 int,则使用第一个 MyFunction,如果 T 不是 int,则使用第二个 MyFunction,但是我收到编译器错误,说“错误:'struct std::enable_if' 中没有名为'type'的类型” . 谁能指出我在这里做错了什么?

4

6 回答 6

32

enable_if之所以有效,是因为模板参数的替换导致了错误,因此从重载决议集中删除了替换,并且编译器只考虑其他可行的重载。

在您的示例中,实例化成员函数时不会发生替换,因为当时模板参数T是已知的。实现您正在尝试的最简单方法是创建一个默认为的虚拟模板参数T并使用它来执行 SFINAE。

template<typename T>
struct Point
{
  template<typename U = T>
  typename std::enable_if<std::is_same<U, int>::value>::type
    MyFunction()
  {
    std::cout << "T is int." << std::endl;
  }

  template<typename U = T>
  typename std::enable_if<std::is_same<U, float>::value>::type
    MyFunction()
  {
    std::cout << "T is not int." << std::endl;
  }
};

编辑:

正如 HostileFork 在评论中提到的那样,原始示例留下了用户为成员函数显式指定模板参数并获得不正确结果的可能性。以下内容应防止编译成员函数的显式特化。

template<typename T>
struct Point
{
  template<typename... Dummy, typename U = T>
  typename std::enable_if<std::is_same<U, int>::value>::type
    MyFunction()
  {
    static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
    std::cout << "T is int." << std::endl;
  }

  template<typename... Dummy, typename U = T>
  typename std::enable_if<std::is_same<U, float>::value>::type
    MyFunction()
  {
    static_assert(sizeof...(Dummy)==0, "Do not specify template arguments!");
    std::cout << "T is not int." << std::endl;
  }
};
于 2012-11-15T16:31:19.067 回答
6

一个简单的解决方案是使用委托给工作人员私有函数:

template<typename T>
struct Point
{

  void MyFunction()
  {
     worker(static_cast<T*>(nullptr)); //pass null argument of type T*
  }

private:

  void worker(int*)
  {
    std::cout << "T is int." << std::endl;
  }

  template<typename U>
  void worker(U*)
  {
    std::cout << "T is not int." << std::endl;
  }
};

Tis时,将调用int第一个函数,因为结果是 type 。在所有其他情况下,将调用 worker 的模板版本。workerstatic_cast<T*>(0)int*

于 2012-11-15T16:39:27.463 回答
1

enable_if仅适用于推导的函数模板参数或专门的类模板参数。你正在做的事情不起作用,因为显然使用 fixed T = int,第二个声明是错误的。

这是可以做到的:

template <typename T>
void MyFreeFunction(Point<T> const & p,
                    typename std::enable_if<std::is_same<T, int>::value>::type * = nullptr)
{
    std::cout << "T is int" << std::endl;
}

// etc.

int main()
{
    Point<int> ip;
    MyFreeFunction(ip);
}

另一种方法是专门Point针对各种类型T,或者将上述自由函数放入嵌套成员模板包装器中(这可能是更“正确”的解决方案)。

于 2012-11-15T16:32:47.407 回答
1

根据 Praetorian 的建议(但不更改函数的返回类型),这似乎可行:

#include <iostream>
#include <type_traits>

template<typename T>
struct Point
{
  template<typename U = T>
  void MyFunction(typename std::enable_if<std::is_same<U, int>::value, U >::type* = 0)
  {
    std::cout << "T is int." << std::endl;
  }

  template<typename U = T>
  void MyFunction(typename std::enable_if<!std::is_same<U, int>::value, float >::type* = 0)
  {
    std::cout << "T is not int." << std::endl;
  }
};

int main()
{
  Point<int> intPoint;
  intPoint.MyFunction();

  Point<float> floatPoint;
  floatPoint.MyFunction();
}
于 2012-11-15T16:44:56.193 回答
1

我认为这遵循@Praetorian 的解决方案,但我发现它更容易:

template<typename T>
struct Point
{
    template<typename U = T>
    std::enable_if_t<std::is_same<U, T>::value && std::is_same<T, int>::value>
    MyFunction()
    {
        std::cout << "T is int." << std::endl;
    }

    template<typename U = T>
    std::enable_if_t<std::is_same<U, T>::value && std::is_same<T, float>::value>
    MyFunction()
    {
        std::cout << "T is not int." << std::endl;
    }
};
于 2020-09-19T17:42:13.337 回答
0

下面的点模板只能使用 int 或 float 作为模板参数 T 进行实例化。

回答这个问题:这里 worker() 的调用完全取决于 method() 调用的模板参数,但您仍然可以控制类型。

    template<typename T>
    struct Point
    {
        static_assert (
              std::is_same<T, int>()  ||
              std::is_same<T, float>()
            );

        template<typename U>
        void method(U x_, U y_)
        {
            if constexpr (std::is_same<T, U>()) {
                worker(x_, y_);
                return;
            }
            // else 
            worker(
                static_cast<T>(x_),
                static_cast<T>(y_)
            );
            return ;
        }


    private:

        mutable T x{}, y{};

        void worker(T x_, T y_)
        {
            // nothing but T x, T y
        }

    };

上面的 worker() 当然会工作,即使它被声明为静态的。出于某种正当理由。对上述内容的其他扩展很少(而且很简单),但让我们坚持答案。

于 2018-05-18T13:10:59.650 回答