英文:
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 <iostream>
#include <functional>
template<typename CMP>
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; // ¯\_(ツ)_/¯
}
int main()
{
const float A = order<std::less<float>>(3.1, 7.2); // COMPILE ERROR
const float B = order<std::greater<float>>(3.1, 7.2);
std::cout << "A=" << A << "\n";
std::cout << "B=" << B << "\n";
return 0;
}
COMPILE ERROR:
$ 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);
^
答案1
得分: 4
你可以像这样返回:
return CMP{}(a, b);
这是因为 std::less
的构造函数不接受任何参数。
CMP
是一种类型,所以你需要首先创建这种类型的对象 CMP{}
,然后才能调用临时对象 CMP
上的 ()
运算符来进行比较。
上述解决方案的作用是:实例化一个 std::less
对象,然后使用参数 a
和 b
调用 operator()
。这个运算符函数接受两个参数,如果 a
较小,它将返回 true(一个布尔表达式)。然后,通过你的三元检查,返回 a
和 b
中较小的值。
英文:
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::less
或std::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;
// ^^^^^^ ----> 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<template<typename> class CMP, typename T>
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ --> 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<std::less>(3.1, 7.2);
// ^^^^^^^^^^^ -----> only specify the funnctor
const auto B = order<std::greater>(3.1, 7.2);
// ^^^^^^^^^^^^^^ --> 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<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) ? 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) ? 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) ? 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) ? 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);
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论