我是否可以仅针对尚未指定该行为的类型限制模板运算符?
Can I restrict a template operator only for types that do not have that behaviour already specified?
假设我想为所有类型实现 operator<<
。我会这样做:
template <typename T>
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
但是由于歧义(在已经指定的 operator<<
是自由函数的情况下),这将不起作用。所以我试图用 concept
:
来限制它
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
这正确地报告了 int
s、std::string
s 和诸如此类的东西是可打印的,但是 std::vector
s 或 some_user_defined_struct
s(没有超载 <<
)不是。
我想要的是将此 concept
与我的(过于通用的)operator<<
:
一起使用
#include <iostream>
#include <vector>
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
template <typename T>
requires (!printable<T>)
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
int main() {
std::cout << std::vector<int>();
}
但这会导致:
In instantiation of 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]':
recursively required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]'
required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]'
required from here
fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum)
6 | out << t;
| ~~~~^~~~
compilation terminated.
好像有一个实例化循环。为了检查我们是否应该使用我的<<
,正在检查printable
,这样做会尝试生成<<
,这会导致循环。
是否有任何机制可以防止这种循环?我们能否以仅在需要时生成 template
的方式来约束类型?至于用例,假设出于某种原因,当有人试图 <<
某些东西到 std::cout
.
时,我绝不希望编译失败
当且仅当未提供操作时,您才能提供操作。这本质上是自递归的。
您可以做的是添加另一层间接。像这样:
template <typename T>
void print(std::ostream& os, T&& t) {
if constexpr (printable<T>) {
os << t;
} else {
os << "DEFAULT";
}
}
我正在解决这个问题,并且能够想出类似于问题描述的内容,唯一的区别是您必须通过 using namespace
指令选择使用它。 (godbolt demo)
#include <iostream>
#include <vector>
#include <utility>
template <typename T>
// T can be a reference type
concept printable = requires(std::ostream& out, T t) {
out << std::forward<T>(t);
};
template <typename T>
requires (!printable<T>)
std::ostream& default_print(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
namespace default_ostream
{
template<typename T>
std::ostream& operator<<(std::ostream& out, T&& t)
requires requires { default_print(out, std::forward<T>(t)); }
{
return default_print(out, std::forward<T>(t));
}
} // namespace default_ostream
int main()
{
using namespace default_ostream;
std::cout << std::vector{ 0, 1, 2 } << '\n';
std::cout << "Hello!\n";
std::cout << 2.234 << '\n';
}
此程序将使用所有 GCC、Clang 和 MSVC 输出以下内容:
DEFAULT
Hello!
2.234
将默认 operator<<
放在单独的命名空间中并将 !printable<T>
要求推迟到 print_generic
似乎可行。有了这个,如果你想要这种行为,你将不得不做 using namespace default_ostream;
,它不能出现在全局范围内,但在功能(或某些命名空间)范围内很好。
之所以可行,是因为 printable
在用作 default_print
的要求时看不到通用 operator<<
,这样就无法为 [=20= 选择] 并避免递归实例化。
当你想使用泛型 operator<<
时,你必须使用 using namespace default_ostream;
将它带入本地范围,因此它参与了重载决议,并且由于它的要求,只有在没有其他 operator<<
可用。
假设我想为所有类型实现 operator<<
。我会这样做:
template <typename T>
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
但是由于歧义(在已经指定的 operator<<
是自由函数的情况下),这将不起作用。所以我试图用 concept
:
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
这正确地报告了 int
s、std::string
s 和诸如此类的东西是可打印的,但是 std::vector
s 或 some_user_defined_struct
s(没有超载 <<
)不是。
我想要的是将此 concept
与我的(过于通用的)operator<<
:
#include <iostream>
#include <vector>
template <typename T>
concept printable = requires(const T& t, std::ostream& out) {
out << t;
};
template <typename T>
requires (!printable<T>)
std::ostream& operator<<(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
int main() {
std::cout << std::vector<int>();
}
但这会导致:
In instantiation of 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]': recursively required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]' required from 'std::ostream& operator<<(std::ostream&, T&&) [with T = const char (&)[8]]' required from here fatal error: template instantiation depth exceeds maximum of 900 (use '-ftemplate-depth=' to increase the maximum) 6 | out << t; | ~~~~^~~~ compilation terminated.
好像有一个实例化循环。为了检查我们是否应该使用我的<<
,正在检查printable
,这样做会尝试生成<<
,这会导致循环。
是否有任何机制可以防止这种循环?我们能否以仅在需要时生成 template
的方式来约束类型?至于用例,假设出于某种原因,当有人试图 <<
某些东西到 std::cout
.
当且仅当未提供操作时,您才能提供操作。这本质上是自递归的。
您可以做的是添加另一层间接。像这样:
template <typename T>
void print(std::ostream& os, T&& t) {
if constexpr (printable<T>) {
os << t;
} else {
os << "DEFAULT";
}
}
我正在解决这个问题,并且能够想出类似于问题描述的内容,唯一的区别是您必须通过 using namespace
指令选择使用它。 (godbolt demo)
#include <iostream>
#include <vector>
#include <utility>
template <typename T>
// T can be a reference type
concept printable = requires(std::ostream& out, T t) {
out << std::forward<T>(t);
};
template <typename T>
requires (!printable<T>)
std::ostream& default_print(std::ostream& out, T&& t) {
return out << "DEFAULT";
}
namespace default_ostream
{
template<typename T>
std::ostream& operator<<(std::ostream& out, T&& t)
requires requires { default_print(out, std::forward<T>(t)); }
{
return default_print(out, std::forward<T>(t));
}
} // namespace default_ostream
int main()
{
using namespace default_ostream;
std::cout << std::vector{ 0, 1, 2 } << '\n';
std::cout << "Hello!\n";
std::cout << 2.234 << '\n';
}
此程序将使用所有 GCC、Clang 和 MSVC 输出以下内容:
DEFAULT
Hello!
2.234
将默认 operator<<
放在单独的命名空间中并将 !printable<T>
要求推迟到 print_generic
似乎可行。有了这个,如果你想要这种行为,你将不得不做 using namespace default_ostream;
,它不能出现在全局范围内,但在功能(或某些命名空间)范围内很好。
之所以可行,是因为 printable
在用作 default_print
的要求时看不到通用 operator<<
,这样就无法为 [=20= 选择] 并避免递归实例化。
当你想使用泛型 operator<<
时,你必须使用 using namespace default_ostream;
将它带入本地范围,因此它参与了重载决议,并且由于它的要求,只有在没有其他 operator<<
可用。