如何在函数定义中将std::less定义为模板参数并使用?

huangapple go评论101阅读模式
英文:

How to define and use std::less as a template argument in a function definition?

问题

合适的C++语法来提供和使用std::less作为模板参数是什么?

#include <iostream>
#include <functional>

template<typename CMP>
float order(float a, float b)
{
    return CMP()(a, b);  // 正确的语法
}

int main() 
{
    const float A = order<std::less<float>>(3.1, 7.2);
    const float B = order<std::greater<float>>(3.1, 7.2);
    std::cout << "A=" << A << "\n";
    std::cout << "B=" << B << "\n";
    return 0;
}

编译错误:

$ g++ -std=c++17 foo.cpp -o foo

foo.cpp:6:12: error: no matching constructor for initialization of 'std::__1::less<float>'
foo.cpp:12:21: note: in instantiation of function template specialization 'order<std::__1::less<float> >' requested here
const float A = order<std::less<float>>(3.1, 7.2);
                ^

希望这有帮助。

英文:

What is the proper C++ syntax to supply and use std::less as a template argument?

#include &lt;iostream&gt;
#include &lt;functional&gt;

template&lt;typename CMP&gt;
float order(float a, float b)
{
    return CMP(a, b) ? a : b;               // COMPILE ERROR                                                               
    // return CMP::operator()(a,b) ? a : b; //  another try
    // return (a CMP b) ? a : b;            // &#175;\_(ツ)_/&#175;
}

int main() 
{
    const float A = order&lt;std::less&lt;float&gt;&gt;(3.1, 7.2);  // COMPILE ERROR
    const float B = order&lt;std::greater&lt;float&gt;&gt;(3.1, 7.2);
    std::cout &lt;&lt; &quot;A=&quot; &lt;&lt; A &lt;&lt; &quot;\n&quot;;
    std::cout &lt;&lt; &quot;B=&quot; &lt;&lt; B &lt;&lt; &quot;\n&quot;;
    return 0;
}

COMPILE ERROR:

$ g++ -std=c++17 foo.cpp -o foo

foo.cpp:6:12: error: no matching constructor for initialization of &#39;std::__1::less&lt;float&gt;&#39;
foo.cpp:12:21: note: in instantiation of function template specialization &#39;order&lt;std::__1::less&lt;float&gt; &gt;&#39; requested here
const float A = order&lt;std::less&lt;float&gt;&gt;(3.1, 7.2);
                ^

答案1

得分: 4

你可以像这样返回:

return CMP{}(a, b);

这是因为 std::less 的构造函数不接受任何参数。

CMP 是一种类型,所以你需要首先创建这种类型的对象 CMP{},然后才能调用临时对象 CMP 上的 () 运算符来进行比较。

上述解决方案的作用是:实例化一个 std::less 对象,然后使用参数 ab 调用 operator()。这个运算符函数接受两个参数,如果 a 较小,它将返回 true(一个布尔表达式)。然后,通过你的三元检查,返回 ab 中较小的值。

英文:

You can just return it like this:

return CMP{}(a,b) ? a : b;

This is because the constructor of std::less does not take any parameters.

CMP is a type, so you need to create an object of this type first, CMP{}, before you can invoke the () operator on the temporary CMP to do the comparison.

What the above solution does is: It instantiates an std::less object and then invokes operator() with the parameters a and b. This operator function then takes the two parameters and returns true if a is the lesser one (a boolean expresion). Then, via your ternary check, the smaller value between a and b is returned.

答案2

得分: 3

以下是翻译好的部分:

用作模板参数供应和使用std::less的正确C++语法是什么?

std::lessstd::greater是函数对象(即functor)。这意味着在调用operator()之前,您需要对它们进行默认构造。因此,返回语句必须是:

return CMP{}(a, b) ? a : b;
//     ^^^^^^ ----> 默认构造函数对象

但是,如果您想节省一些输入,可以使用模板模板参数来为order指定CMP。可以选择提供一个模板参数(即typename T),以使其更通用(如果有需要):

template<template<typename> class CMP, typename T>
//       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --> 模板模板参数
constexpr auto order(T a, T b) 
{
    return CMP{}(a, b) ? a : b;
}

现在可以这样调用order

const auto A = order<std::less>(3.1, 7.2); 
//                  ^^^^^^^^^^^ --> 只指定functor
const auto B = order<std::greater>(3.1, 7.2);
//                  ^^^^^^^^^^^^^^ --> 只指定functor
英文:

>What is the proper C++ syntax to supply and use std::less as a template argument?

The std::less or std::greater are functors (i.e. function object). This means, prior to the operator() call, you need to default constructed them. Therefore, the return statement must be

return CMP{}(a,b) ? a : b;
//     ^^^^^^ ----&gt; default construct the function object

However, if you want to save some typing, you might use template-template parameters for the order. Optionally, you can provide a template parameter(i.e typename T), to make it more generic (If intended) :

template&lt;template&lt;typename&gt; class CMP, typename T&gt;
//       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --&gt; template-template argument
constexpr auto order(T a, T b) 
{
    return CMP{}(a, b) ? a : b;
}

Now you call the order as

const auto A = order&lt;std::less&gt;(3.1, 7.2); 
//                  ^^^^^^^^^^^ -----&gt; only specify the funnctor
const auto B = order&lt;std::greater&gt;(3.1, 7.2);
//                  ^^^^^^^^^^^^^^ --&gt; only specify the funnctor

答案3

得分: 2

A well-designed interface would not prevent the use of a stateful comparison predicate. And for free, you get a nicer implementation of the other alternatives.

template<typename CMP> float order(float a, float b, CMP cmp = {}) {
   return cmp(a, b) ? a : b;
}

NOTE 1:

Despite popular belief, if CMP is not default constructible, the syntax will still work; but of course, you will need to pass a way to construct it. Which is good.

test:

#include <iostream>
#include <functional>

template<typename CMP>
float order(float a, float b, CMP cmp = {}) {
    return cmp(a, b);                                            
}

template<class T>
struct LESS : std::less<T> {
    LESS() = delete;
    LESS(const char*) {}
};

int main() {
    const float A = order<std::less<float>>(3.1, 7.2);
    const float B = order<std::greater<float>>(3.1, 7.2);
    const float C = order<LESS<float>>(3.1, 7.2, "less");
    // const float D = order<LESS<float>>(3.1, 7.2);  // compile error, good!

    std::cout << "A=" << A << "\n";
    std::cout << "B=" << B << "\n";
    return 0;
}

NOTE 2:

A better-designed interface will have a default order:

template<typename CMP = std::less<float>>
float order(float a, float b, CMP cmp = {}) {
    return cmp(a, b);                                            
}

NOTE 3:

Technically, if the class has an explicit default constructor, one needs something more verbose for this to work, but I wouldn't implement this way because if the default constructor is explicit, there must be a good reason for it, and we are basically overriding the intended behavior of the class designer.

template<typename CMP = std::less<float>>
float order(float a, float b, CMP cmp = CMP{}) {  // MMM, good idea?
    return cmp(a, b);                                            
}

Bonus:

Some people (including STL, https://en.cppreference.com/w/cpp/algorithm/sort) do not tolerate default parameters:

template<typename CMP>
float order(float a, float b, CMP cmp) {
    return cmp(a, b);                                            
}

template<typename CMP>
float order(float a, float b) {
    return order<CMP>(a, b, {}); // or CMP{});
}

float order(float a, float b) {
    return order<std::less<float>>(a, b);
}
英文:

A well-designed interface would not prevent the use of a stateful comparison predicate.
And for free, you get a nicer implementation of the other alternatives.

template&lt;typename CMP&gt; float order(float a, float b, CMP cmp = {}) {
   return cmp(a, b) ? a : b;
}

NOTE 1:

Despite popular belief, if CMP is not default constructible, the syntax will still work; but of course, you will need to pass a way to construct it.
Which is good.

test:

#include &lt;iostream&gt;
#include &lt;functional&gt;

template&lt;typename CMP&gt;
float order(float a, float b, CMP cmp = {}) {
    return cmp(a,b) ? a : b;                                            
}

template&lt;class T&gt;
struct LESS : std::less&lt;T&gt; {
    LESS() = delete;
    LESS(const char*) {}
};

int main() {
    const float A = order&lt;std::less   &lt;float&gt;&gt;(3.1, 7.2);
    const float B = order&lt;std::greater&lt;float&gt;&gt;(3.1, 7.2);
    const float C = order&lt;     LESS   &lt;float&gt;&gt;(3.1, 7.2, &quot;less&quot;);
    // const float D = order&lt;     LESS   &lt;float&gt;&gt;(3.1, 7.2);  // compile error, good!

    std::cout &lt;&lt; &quot;A=&quot; &lt;&lt; A &lt;&lt; &quot;\n&quot;;
    std::cout &lt;&lt; &quot;B=&quot; &lt;&lt; B &lt;&lt; &quot;\n&quot;;
    return 0;
}

NOTE 2:

A better-designed interface will have a default order:

template&lt;typename CMP = std::less&lt;float&gt; &gt;
float order(float a, float b, CMP cmp = {}) {
    return cmp(a,b) ? a : b;                                            
}

NOTE 3:

Technically, if the class has an explicit default constructor, one needs something more verbose for this to work, but I wouldn't implement this way because, if the default constructor is explicit, there must be a good reason for it, and we are basically overriding the intended behavior of the class designer.

template&lt;typename CMP = std::less&lt;float&gt; &gt;
float order(float a, float b, CMP cmp = CMP{}) {  // MMM, good idea?
    return cmp(a,b) ? a : b;                                            
}

Bonus:

Some people (including STL, https://en.cppreference.com/w/cpp/algorithm/sort) do not tolerate default parameters:

template&lt;typename CMP&gt;
float order(float a, float b, CMP cmp) {
    return cmp(a,b) ? a : b;                                            
}

template&lt;typename CMP&gt;
float order(float a, float b) {
    return order&lt;CMP&gt;(a, b, {}); // or CMP{});
}

float order(float a, float b) {
    return order&lt;std::less&lt;float&gt;&gt;(a, b);
}

huangapple
  • 本文由 发表于 2023年7月14日 05:48:31
  • 转载请务必保留本文链接:https://go.coder-hub.com/76683434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定