有没有摆脱这种歧义的方法?

时间:2018-07-28 02:53:20

标签: c++ ambiguous-call

考虑这个(而不是)简单的示例:

#include <iostream>

struct Out {
    int value;
};
template<class Sink> decltype(auto) operator<<(Sink &&s, Out const &out) {
    return out.value > 0? s << out.value : s;
}

struct In {
    std::ostream &sink;
    template<typename T> In &operator<<(T const &t) {
        return sink << t, *this;
    }
};

int main() {
    In in{std::cout};
    in << (1 << Out{3}) << '\n';    // Ok
    in << Out{42} << '\n';
        // error: use of overloaded operator '<<' is ambiguous
        // (with operand types 'In' and 'Out')
}

这种歧义可以解决吗?我们有两个类,每个类都定义一个这样的运算符重载以将其转发到其内部类型(这些类是由两个不同的人独立设计的,而另一个人则试图在同一应用程序中使用它们。)可以按照其他运算符的形式重新定义,例如,这里没有必要放弃A的{​​{1}}并尝试将friend operator<<转换为A;并使用某种复杂的SFINAE来排除某些重载似乎仍然无济于事。

2 个答案:

答案 0 :(得分:2)

您可能会创建其他更合适的重载:

decltype(auto) operator<<(In& in, Out const &out) {
    return in.operator<<(out);
}

decltype(auto) operator<<(In&& in, Out const &out) {
    return in.operator<<(out);
}

答案 1 :(得分:0)

某些半老式SFINAE似乎可以解决问题,因为在某种意义上,它已被gcc和clang接受(并且它们均打印“ 8”和“ 42”):

#include <iostream>
#include <utility>

template<typename S, typename T> class can_shl {
    using Left = char;
    struct Right { char c[2]; };
    template<typename U> static constexpr decltype(
            std::declval<S>() << std::declval<U>(), Left{}) 
        has_it(U &&);
    static constexpr Right has_it(...);
public:
    static constexpr bool value = sizeof(has_it(std::declval<T>())) == 1;
};

struct Out {
    int value;
};
template<class Sink> auto operator<<(Sink &&s, Out const &out) 
    -> std::enable_if_t<!can_shl<Sink, Out>::value, decltype(out.value > 0? s << out.value : s)> 
{   
    return out.value > 0? s << out.value : s;
}

struct In {
    std::ostream &sink;
    template<typename T> In &operator<<(T const &t) {
        return sink << t, *this;
    }
};

int main() {
    In in{std::cout};
    in << (1 << Out{3}) << '\n';    // Ok
    in << Out{42} << '\n';
}

我个人对“仅在不自行编译的情况下才允许使用此方法”感到不太自信(这是一个(静态)UB吗?如果我是C ++标准,我会怎么说?)