代码之家  ›  专栏  ›  技术社区  ›  bipll

有办法摆脱这种模棱两可的局面吗?

  •  2
  • bipll  · 技术社区  · 6 年前

    考虑这个(相当)简单的例子:

    #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 friend operator<< 试着改变 一个 int 使用某种复杂的sfinae来排除一些重载看起来仍然没有帮助。

    2 回复  |  直到 6 年前
        1
  •  2
  •   Jarod42    6 年前

    您可能会创建其他重载,以便更好地匹配:

    decltype(auto) operator<<(In& in, Out const &out) {
        return in.operator<<(out);
    }
    
    decltype(auto) operator<<(In&& in, Out const &out) {
        return in.operator<<(out);
    }
    
        2
  •  0
  •   bipll    6 年前

    一些半古老的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++标准,我会说什么?