c++ 对std::variant [duplicate]使用重载〈〈时出现问题

h5qlskok  于 2023-03-10  发布在  其他
关注(0)|答案(1)|浏览(155)

此问题在此处已有答案

Argument dependent name lookup and typedef(4个答案)
ADL with typedefs from another namespace(7个答案)
Why does arguement dependent lookup not work for type aliasing in c++?(1个答案)
Argument-dependent lookup behaving unexpectedly on types aliased from another namespace(1个答案)
Argument Dependent Lookup and stream operators overloading(1个答案)
4天前关闭。
我为别名std::variantA::Var)重载了一个<<。我还有一个模板函数,它定义在另一个名称空间C::Wrapper中的一个类中,它只是将其参数转发给std::ostream
我试图从A中的类A::Foo中定义的另一个函数调用它,但是这给了我编译器错误。

#include <iostream>
#include <variant>

namespace C {
  struct Wrapper {
    template<typename T>
    auto& operator<<(T&& v) {
      std::cout << std::forward<T>(v);
      return *this;
    }
  };
}

namespace A {
  using Var = std::variant<bool, int>;

  auto& operator<<(std::ostream& os, const A::Var& v) {
    std::visit([&os](auto i) { os << i; }, v);
    return os;
  }

  struct Foo {
    void m() {
      C::Wrapper wrap;
      Var v{3};
      wrap << "hi"; // works
      wrap << v; // compiler error
    }
  };
}

int main() { 
  A::Foo a;
  a.m();
}

g++ -std=c++17会产生以下错误:

main.cpp: In instantiation of ‘auto& C::Wrapper::operator<<(T&&) [with T = std::variant<bool, int>&]’:
main.cpp:27:11:   required from here
main.cpp:8:15: error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘std::variant<bool, int>’)
    8 |     std::cout << std::forward<T>(v);

... many candidate functions none including my overloaded << for A::Var

我希望它能成功编译并在运行时打印3,我试着让所有的定义都不符合要求,删除const限定符,使重载的<<成为全局的,但这些都不起作用。
我怎样才能修正这个错误,同时保留命名空间和类结构?

  • 更新 *:

通过在全局名称空间中定义<<,代码可以使用gcc进行编译,如下所示,但它会因clang而失败:

namespace A {
  using Var = std::variant<bool, int>;
}

auto &operator<<(std::ostream &os, const A::Var &v) {
  std::visit([&os](auto i) { os << i; }, v);
  return os;
}

namespace A {
  struct Foo {
    void m() {
      C::Wrapper wrap;
      A::Var v{3};
      wrap << "hi"; // works
      wrap << v;    // compiler error
    }
  };
} // namespace A

clang++ -std=c++17错误:

main.cpp:7:15: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
    std::cout << std::forward<T>(v);
              ^
main.cpp:28:10: note: in instantiation of function template specialization 'C::Wrapper::operator<<<std::variant<bool, int> &>' requested here
    wrap << v;    // compiler error
         ^
main.cpp:17:7: note: 'operator<<' should be declared prior to the call site
auto &operator<<(std::ostream &os, const A::Var &v) {
      ^
1 error generated.

为什么它可以和gcc一起使用,而不能和clang一起使用?

68bkxrlz

68bkxrlz1#

参数相关查找(ADL)不考虑与别名关联的名称空间(这也没有意义,编译器是否应该记住所有名称空间都有std::variant<bool, int>的别名,然后突然认为所有声明在那里的函数都属于std::variant<bool, int>?)
使Var成为一个合适的独立类型。要么使它成为一个具有私有std::variant<bool, int>成员的类,然后可以实现和转发所需的std::variant接口部分,要么让Varstd::variant<bool, int>公开继承并继承构造函数,这样做的好处是std::visit仍然可以开箱即用。
然后,当您将<<应用于Var时,ADL将考虑命名空间A中的operator<<重载。

相关问题