能否获取内置标准运算符的函数指针?

17

我想引用内置运算符的函数指针,但是我不知道如何指定特定类型的重载。

我有以下模板类签名:

template<typename ParamsType, typename FnCompareType>
class MyAction
{
public:
    MyAction(ParamsType& arg0, ParamsType& arg1, FnCompareType& fnCpmpare) 
    : arg0_(arg0), arg1_(arg1), fnCompare_(fnCpmpare) {}

    bool operator()()
    {
        if((*fnCompare_)(arg0_,arg1_)
        {
            // do this
        }
        else
        {
            // do s.th. else
        }
    }

private:
    ParamsType& arg0_;
    ParamsType& arg1_;
    FnCompareType& fnCompare_;
}

我想使用类似这样的语法:

void doConditional(int param1, int param2)
{
    MyAction<int,&::operator>=> action(param1,param2);
    if(action())
    {
        // Do this
    }
    else
    {
        // Do that
    }
}

但这不会编译:

error:::operator>=’ has not been declared

我应该怎么称呼这些内在的静态操作?


是的,pixelchemist 给出了非常详尽和完整的答案。在我看来,你应该修改你的示例代码,使其能够编译而不依赖于任何第三方库。从我阅读你的代码的方式来看,ACTION_P1 应该是一个函数名。然而,我没有看到返回类型。此外,它的参数只有名称,没有类型。 - Code-Apprentice
@MonadNewb ACTION_P1 应该是一个函数名 实际上,它是定义模板类的宏。但我会尝试将其更改为常见示例,并使用简单函数来代替。 - πάντα ῥεῖ
这是一个定义模板类的宏。换句话说,你的示例依赖于第三方库,读者需要安装该库才能编译示例代码。这个宏对于关于操作符和函数指针的问题重要吗?如果不是,那么这是一个额外的细节,使得这个问题略微不太适合作为“规范”问题。 - Code-Apprentice
@MonadNewb,我不确定我的编辑是否正确地反映了问题,请您看一下... - πάντα ῥεῖ
那看起来好多了。 - Code-Apprentice
显示剩余4条评论
3个回答

23

内置运算符

为什么无法创建它们的函数指针:

C++11,§13.6/1,[over.built]

在本子句中指定了代表第5条款中定义的内置运算符的候选运算符函数。这些候选函数参与操作符重载决议过程,如13.3.1.2所述,并且不用于其他任何目的。

内置运算符(内置类型的运算符)不是真正的运算符函数。因此,不能有指向它们的函数指针。您也不能使用operator<(A,B)语法调用它们。它们只参与重载决议,但编译器会将它们直接翻译成相应的汇编/机器指令,而不进行任何形式的“函数调用”。

解决此问题的方法:

user1034749已经回答了这个问题,但是为了完整起见:

标准在§20.8,[function.objects]中定义了许多函数对象,即:

  • 算术运算
  • 比较
  • 逻辑运算
  • 位运算

函数对象是函数对象类型的对象。在算法模板(第25条)中期望传递函数指针的地方,指定接口接受函数对象。这不仅使算法模板与函数指针一起工作,而且还使它们能够使用任意函数对象。

C++11, §20.8.5,[comparisons]

  • equal_to
  • not_equal_to
  • greater, less
  • greater_equal
  • less_equal

这些是模板化的函数对象,在其operator()函数中衰减为相应的运算符。它们可以用作函数指针参数。

user1034749是正确的,我想声明:没有其他方法,这些在使用上与“原始”函数指针完全等效。参考文献。

标准类类型运算符

您可以使用标准库运算符作为函数指针(作为“真实函数”存在)。

但是,您将需要引用模板的相应实例。编译器将需要适当的提示来推断正确的模板。

这对我在MSVC 2012上使用std::basic_stringoperator+有效。

template<class Test>
Test test_function (Test const &a, Test const &b, Test (*FPtr)(Test const &, Test const &))
{
   return FPtr(a, b);
}

int main(int argc, char* argv[])
{
   typedef std::char_traits<char> traits_t;
   typedef std::allocator<char> alloc_t;
   std::basic_string<char, traits_t, alloc_t> a("test"), b("test2");
   std::cout << test_function<std::basic_string<char, traits_t, alloc_t>>(a, b, &std::operator+) << std::endl;
   return 0;
}
如果省略 test_function 的模板参数,这将导致失败(至少对于 MSVC 2012)。

这看起来非常像这个问题的规范答案! - πάντα ῥεῖ

9
您可以使用与C++标准库中使用的相同的解决方案:
std::sort (numbers, numbers+5, std::greater<int>());

更大的地方

template <class T> struct greater : binary_function <T,T,bool> {
    bool operator() (const T& x, const T& y) const {return x>y;}
};

在你的情况下http://www.cplusplus.com/reference/functional/greater_equal/,关于内置运算符的参考。
你可以引用任何类的现有operator<(当然,如果它们不是私有的、受保护的或者你的类/函数不是友元的话)。 但是对于内置类型(bool、short、int、double),无法引用operator<。 即使不看C++标准,也可以从我上面的文字中看出。

谢谢!我已经找到如何使用它了。抱歉,我编辑了我的问题,以获得一个通用的答案,如果可能的话,可以引用这些运算符函数。 - πάντα ῥεῖ
@g-makulik 标准库提供了命名函数,这些函数包装了大多数内置运算符。 - Code-Apprentice
1
@MonadNewb 再说一遍:我知道。但是这些和“原始”函数指针有点不一样,对吧?这些是带有适当的operator()定义的结构体,并且你需要一个实例来调用它们。 - πάντα ῥεῖ
@g-makulik,“'raw'函数指针”是什么意思?也许这就是混淆的原因所在。 - Code-Apprentice
@MonadNewb 对我来说问题是如何将调用语法推迟,就像我的原始示例中一样((*fnCompare)(arg0->x(),arg0->y()))。推迟应该会对任何静态或全局函数的“原始”函数指针正确地工作。我不得不稍微改变调用语法才能让它正常工作。 - πάντα ῥεῖ
显示剩余2条评论

0

对于 fghj 提供的解决方案的扩展,可以适用于类似 +=/-= 等赋值运算符的情况,方法是将其包装成与标准变体类似的形式。然后你可以这样做:

#include <iostream>

template <typename T>
struct assign_plus {
    void operator() const (T& a, const T& b){
        a += b;
    }
};

template <typename T>
struct assign_minus {
    void operator() const (T& a, const T& b){
        a -= b;
    }
};


template<template <class T> class O> requires requires(int& a, const int& b){
    { O<int>{}(a,b) };
}
void example(int& a, const int& b){
    O<int>{}(a,b);
}

int main(){
    int a = 5;
    int b = 6;
    example<assign_plus>(a,b);
    std::cout << a << "\n";
    example<assign_minus>(a,b);
    std::cout << a << "\n";
    return 0;
}

在考虑到 c++20 兼容性的情况下,约束条件可以保留或移除。这些约束条件还可以扩展,要求对于自定义类型,a += b 是有效的。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接