为什么 C++ 中用于算术运算的函数对象被实现为模板?
Why functional objects in C++ for arithmetic operations are implemented as templates?
我想知道,为什么 c++ 中的函数对象被实现为模板化的,并且 void
作为默认类型,因为 c++14.
例如:
- https://en.cppreference.com/w/cpp/utility/functional/plus
- https://en.cppreference.com/w/cpp/utility/functional/minus
当被operator()
调用时,这个对象实际上执行算术运算+
、-
、*
、/
。
operator()
必须是模板才能使用不同的类型作为参数,但为什么结构必须是?
编辑
我可以创建一个运算符 std::plus<>
,它可能适用于 operator()
中的不同类型:
struct Foo{
int foo;
};
Foo operator+(const Foo& lhs, const Foo& rhs){
return {2 * lhs.foo + 3 * rhs.foo};
}
std::ostream& operator<<(std::ostream& os, const Foo& f){
std::cout << f.foo;
return os;
}
int main()
{
auto op = std::plus<>();
std::cout << op(5, 3) << "\n";
std::cout << op(3.14, 2.71) << "\n";
std::cout << op(Foo(2), Foo(3)) << "\n";
}
这给出了预期的输出。或者情况可能是,最初指定类型后您会得到更优化的东西?
这是一种设计选择。如果你指定的类型没有模板 operator()
,而是整个 class 是一个模板。 operator()
就像
constexpr T operator()(const T &lhs, const T &rhs) const
{
return lhs + rhs;
}
这在几个方面不同于模板 operator()
。
如果我们传递一个 std::plus<int>
,它是一个加函子,专门用于 int
,没有别的。
如果我们在不指定类型的情况下传递 std::plus<>
,那么它将具有模板化的 operator()
。该仿函数可以将其 operator()
应用于任何有效类型。
从我的脑海中限制类型的一些优点:
由于指定了类型,仿函数可以毫无问题地处理隐式转换。
你知道仿函数不会默默地做我们不希望它做的事情。它只会在 T
上进行加法运算。
编辑
行为会有所不同的一些示例。
#include <iostream>
#include <functional>
#include <string>
struct Foo {};
int main()
{
auto stringadd = std::plus<std::string>{};
auto anyadd = std::plus<>{};
std::cout << stringadd("hey ", "you") << '\n';
//std::cout << anyadd("hey ", "you") << '\n'; // error: no match for call to '(std::plus<void>) (const char [5], const char [4])'
//std::cout << stringadd("hey ", 1) << '\n'; // error: no match for call to '(std::plus<std::__cxx11::basic_string<char> >) (const char [5], int)'
std::cout << anyadd("hey ", 1) << '\n';
}
我想知道,为什么 c++ 中的函数对象被实现为模板化的,并且 void
作为默认类型,因为 c++14.
例如:
- https://en.cppreference.com/w/cpp/utility/functional/plus
- https://en.cppreference.com/w/cpp/utility/functional/minus
当被operator()
调用时,这个对象实际上执行算术运算+
、-
、*
、/
。
operator()
必须是模板才能使用不同的类型作为参数,但为什么结构必须是?
编辑
我可以创建一个运算符 std::plus<>
,它可能适用于 operator()
中的不同类型:
struct Foo{
int foo;
};
Foo operator+(const Foo& lhs, const Foo& rhs){
return {2 * lhs.foo + 3 * rhs.foo};
}
std::ostream& operator<<(std::ostream& os, const Foo& f){
std::cout << f.foo;
return os;
}
int main()
{
auto op = std::plus<>();
std::cout << op(5, 3) << "\n";
std::cout << op(3.14, 2.71) << "\n";
std::cout << op(Foo(2), Foo(3)) << "\n";
}
这给出了预期的输出。或者情况可能是,最初指定类型后您会得到更优化的东西?
这是一种设计选择。如果你指定的类型没有模板 operator()
,而是整个 class 是一个模板。 operator()
就像
constexpr T operator()(const T &lhs, const T &rhs) const
{
return lhs + rhs;
}
这在几个方面不同于模板 operator()
。
如果我们传递一个 std::plus<int>
,它是一个加函子,专门用于 int
,没有别的。
如果我们在不指定类型的情况下传递 std::plus<>
,那么它将具有模板化的 operator()
。该仿函数可以将其 operator()
应用于任何有效类型。
从我的脑海中限制类型的一些优点:
由于指定了类型,仿函数可以毫无问题地处理隐式转换。
你知道仿函数不会默默地做我们不希望它做的事情。它只会在 T
上进行加法运算。
编辑
行为会有所不同的一些示例。
#include <iostream>
#include <functional>
#include <string>
struct Foo {};
int main()
{
auto stringadd = std::plus<std::string>{};
auto anyadd = std::plus<>{};
std::cout << stringadd("hey ", "you") << '\n';
//std::cout << anyadd("hey ", "you") << '\n'; // error: no match for call to '(std::plus<void>) (const char [5], const char [4])'
//std::cout << stringadd("hey ", 1) << '\n'; // error: no match for call to '(std::plus<std::__cxx11::basic_string<char> >) (const char [5], int)'
std::cout << anyadd("hey ", 1) << '\n';
}