运算符重载的模板化定义

时间:2014-02-26 11:51:00

标签: templates c++11 sfinae

我有一些函数对象,每个函数对象都定义了operator()的各种双参数重载:

class Foo {};
class Bar {};

struct Functor1 {
    double operator()(const Foo &, const Bar &)  { return 2.2; }
    double operator()(const Bar &, const Foo &)  { return 3.1; }
};

struct Functor2 {
    int operator()(const Foo &, const Foo &)  { return 2; }
    int operator()(const Bar &, const Bar &)  { return 3; }
};

现在我要做的是编写一个实用程序,让我将这些仿函数逐个附加到二元运算符。这是我的尝试:

#define ATTACH_FUNCTOR_TO_OPERATOR(OPERATOR, FUNCTOR) \
    template<typename Operand1, typename Operand2> \
    decltype(FUNCTOR(std::declval<Operand1>(), std::declval<Operand2>())) \
    operator OPERATOR(const Operand1 &operand1, const Operand2 &operand2) { \
        return FUNCTOR(operand1, operand2); \
    }

第一次运作正常:

ATTACH_FUNCTOR_TO_OPERATOR(+, Functor1());

但是当我使用相同的运算符添加第二次调用时:

ATTACH_FUNCTOR_TO_OPERATOR(+, Functor2());

然后Visual Studio 2013告诉我“功能模板已经定义”。

请注意,对于任何一对操作数,只有一个+的实例,其返回类型decltype(FUNCTOR(std::declval<Operand1>(), std::declval<Operand2>()))可以解析为任何特定类型。我预计任何其他尝试过的实例都会无声地失败并且不会造成任何麻烦 - 也就是SFINAE。

我也尝试过各种风格的enable_if,但没有成功。

在标准C ++ 11中有合法的方法吗?

有没有办法在VS2013中运行?

2 个答案:

答案 0 :(得分:4)

这是VC ++中的known bug。我们可以将它减少到以下自包含的例子:

template<typename T> void f(decltype(T::a)) {}
template<typename T> void f(decltype(T::b)) {}
  

source_file.cpp(2):错误C2995:'void f(unknown-type)':函数   模板已经定义           source_file.cpp(1):参见'f'的声明

问题似乎是VC ++认为“ unknown-type ”类型(即来自decltype的类型)等同于确定函数模板定义是否为重新定义。

一种解决方法是使用唯一的未使用类型消除模板的歧义:

#define ATTACH_FUNCTOR_TO_OPERATOR(OPERATOR, FUNCTOR) \
    template<typename Operand1, typename Operand2> \
    typename std::conditional<true, \
        decltype(FUNCTOR(std::declval<Operand1>(), std::declval<Operand2>())), \
        std::integral_constant<int, __LINE__>>::type \
    operator OPERATOR(const Operand1 & operand1, const Operand2 &operand2) { \
        return FUNCTOR(operand1, operand2); \
    }

答案 1 :(得分:2)

以下简化示例在ideone中编译得很好并返回正确的结果,所以我只能想象它是某种VS2013错误。但是我不是C ++ - 标准专家所以我不知道是否需要编译器来处理这种情况。

#include <iostream>
#include <type_traits>
using namespace std;

class Foo {};
class Bar {};

struct Functor1
{
    static double functor(const Foo &) { return 2.2; }
};

struct Functor2
{
    static int functor(const Bar &) { return 3; }
};

template<typename Operand>
auto test(const Operand &operand) -> decltype(Functor1::functor(operand))
{
    return Functor1::functor(operand);
}

template<typename Operand>
auto test(const Operand &operand) -> decltype(Functor2::functor(operand))
{
    return Functor2::functor(operand);
}

int main()
{
    cout << test(Foo()) << endl << test(Bar());
    return 0;
}

如果我在VS2013中编译它,我会得到同样的错误:“函数模板已经定义”。即使模板从未在整个程序中实例化,也会显示此错误。我只能假设编译器甚至没有尝试找到正确的重载。

不幸的是,我还没有提出解决方案,使用enable_if似乎失败的原因相同。

另请参阅:http://ideone.com/JbDKDF

修改

我发现了一个类似的问题,答案非常有趣:

VS2012 SP1 (+november pack) unknown-type errors (alike C::a(T &&...) )