【问题标题】:Check whether an operator is overloaded in C++ [closed]检查运算符是否在 C++ 中重载 [关闭]
【发布时间】:2018-08-31 22:38:12
【问题描述】:

我正在编写一个用于混洗的模板函数,我想在尝试使用之前检查“小于”运算符是否在任意数据结构上重载。这可能吗?

【问题讨论】:

  • 如果您需要使用小于运算符进行比较,请执行此操作。由您的类或函数的用户来确保存在正确的重载。
  • 至于“洗牌”,std::shuffle有什么问题?
  • 好吧,洗牌不是重点。我想知道是否可以检查运算符是否在任意数据结构上重载。
  • 提出一个具体问题并发布minimal reproducible example 进行说明。
  • 只是不要使用运算符检查 before。使用运算符 is 进行检查。如果该类没有实现它,用户将从编译器获得(或多或少)清晰的错误消息

标签: c++ templates error-handling operator-overloading


【解决方案1】:

我们可以使用Detection Idiom来测试T < T在编译时是否格式正确。

为了便于阅读,我使用的是experimental::is_detected,但您可以在 C++11 中使用 voider 模式自行开发。

首先,它适用于 < 格式正确的类:

struct Has_Less_Than{
    int value;  
};

bool operator < (const Has_Less_Than& lhs, const Has_Less_Than& rhs) {return lhs.value < rhs.value; }

然后一个不是:

struct Doesnt_Have_Less_Than{
    int value;
};
// no operator < defined

现在,对于检测成语部分:我们尝试获取“理论”比较结果的类型,然后询问is_detected

template<class T>
using less_than_t = decltype(std::declval<T>() < std::declval<T>());

template<class T>
constexpr bool has_less_than = is_detected<less_than_t, T>::value;


int main()
{
    std::cout << std::boolalpha << has_less_than<Has_Less_Than> << std::endl; // true
    std::cout << std::boolalpha << has_less_than<Doesnt_Have_Less_Than> << std::endl; // false
}

Live Demo

如果您有可用的 C++17,则可以利用 constexpr if 进行测试:

if constexpr(has_less_than<Has_Less_Than>){
    // do something with <
}
else{
    // do something else
    
}

它之所以有效是因为 constexpr if 在编译时进行评估,并且编译器只会编译所采用的分支。


如果您没有可用的 C++17,则需要使用辅助函数,可能带有标记调度:

template<class T>
using less_than_t = decltype(std::declval<T>() < std::declval<T>());

template<class T>
using has_less_than = typename is_detected<less_than_t, T>::type;

template<class T>
void do_compare(const T& lhs, const T& rhs, std::true_type) // for operator <
{
    std::cout << "use operator <\n";
}

template<class T>
void do_compare(const T& lhs, const T& rhs, std::false_type)
{
    std::cout << "Something else \n";
}

int main()
{
    Has_Less_Than a{1};
    Has_Less_Than b{2};
    
    do_compare(a, b, has_less_than<Has_Less_Than>{});
    
    Doesnt_Have_Less_Than c{3};
    Doesnt_Have_Less_Than d{4};
    do_compare(c, d, has_less_than<Doesnt_Have_Less_Than>{});
}

Demo


如果您有可用的 C++20,我们可以通过 concept 轻松完成此操作:

template<class L, class R=L>
concept has_less_than = requires(const L& lhs, const R& rhs)
{
    {lhs < rhs} -> std::same_as<bool>;
};

这个概念可以理解为“给定两种可能不同的类型,如果在两个 const 引用之间调用 operator&lt; 返回一个布尔值,则该概念是 true。否则它是 false。”

您可以稍微摆弄一下,例如,将 same_as 更改为 convertible_to(如果出于某种原因您希望 operator&lt; 返回一个可以转换为布尔值的整数。

C++20 Demo

这个概念可以用来代替enable_if (requires my_concept&lt;T&gt;),也可以转换成编译时的布尔值,比如true_typefalse_type(所以它可以在if constexpr 中使用)

【讨论】:

  • 哦,我完全误解了这个问题。好答案!
  • @LightnessRacesinOrbit:谢谢。我一开始也误会了。听起来 OP 只需要尝试使用 &lt; 然后编译器会告诉他们是否可能,但后来我看到了模板标签。
  • 这太好了,谢谢!
  • @AndyG:我什至不知道我是否可以解释我在想的废话?
  • @LightnessRacesinOrbit 问题中没有写到即使缺少重载也需要编译良好。所以不,我不认为你误解了这个问题,你的评论绝对没问题...安迪的优点是提出了从未明确提出过的问题,但打算问...
猜你喜欢
  • 2015-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-02-22
  • 2014-07-06
相关资源
最近更新 更多