我想使用模板定义operator<<
的特化,但如果已经为某些数据类型定义了,我不希望它破坏此运算符的行为。
enum State {Open, Locked};
enum Input {Coin, Push};
std::string ToString(State c){
switch (c) {
case Locked : return "Locked";
case Open : return "Open";
}
}
std::string ToString(Input c){
switch (c) {
case Coin : return "Coin";
case Push : return "Push";
}
}
template<typename T> //obviously, a bad idea
std::ostream& operator<<(std::ostream& s, T c) {
return s<<ToString(c);
}
稍后在代码I中,我想使用:
int main() {
std::cout<<Coin<<std::endl;
std::cout<<Open<<std::endl;
std::cout<<std::string("normal string")<<std::endl;
}
不出所料,上面给出了编译错误:
error: ambiguous overload for ‘operator<<’ (operand types are ‘std::ostream {aka std::basic_ostream<char>}’ and ‘std::string {aka std::basic_string<char>}’)
std::cout<<std::string("normal string")<<std::endl;
(更多关注)
问:如果函数/运算符已经有定义,如何告诉编译器忽略模板?
答案 0 :(得分:6)
您可以使用SFINAE使模板实例化仅对ToString()
重载所支持的类型有效,例如
template<typename T, typename = decltype(ToString(std::declval<T>()))>
std::ostream& operator<<(std::ostream& s, T c) {
return s<<ToString(c);
}
答案 1 :(得分:2)
要添加@songyuanyao answered,请再做两件事:
decltype
中的标识符。即像decltype(MyNS::ToString(std::declval<T>()))
。您的打印语句仍然可以用于ADL,但如果您的运算符在某种程度上是一个类型也在中定义ToString
的候选者,则您不会对查找规则执行错误操作另一个名称空间。 1
1 如果您的命名空间中有任何模板,则ADL也会考虑其参数的命名空间。在非限定查找期间,这可能会使您受到另一个ToString
定义的支配。
答案 2 :(得分:0)
我发现我可以使用 C ++概念
来实现template<typename T>
concept bool HasToString = requires(T a) {
{ ToString(a) } -> std::string;
};
std::ostream& operator<<(std::ostream& s, HasToString c) {
return s<<ToString(c);
}
这需要 gcc.6 和-fconcepts
编译标记。