I am observing a weird behavior, which I cannot explain.The following code (a simplified version of the actual code) compiles correctly in gcc 11.4, and the argument "val" inside the read() method of MyStruct is implicitly converted to a Dummy using the converting constructor.
#include <iostream>namespace secret_impl_space{ template <typename T> struct MyStruct { T val; MyStruct(T v): val(v) {} // the val below may be implicitly converted to Dummy if operator >> is missing from T virtual std::istream& read(std::istream& i) { i >> val; return i; } }; struct Dummy { template<typename T> Dummy(const T& v){}; }; inline std::istream& operator>>(std::istream& i, const Dummy& v) { std::cout << "Using Dummy" << std::endl; return i; }}struct A {int a;};int main(){ A aa{1}; secret_impl_space::MyStruct<A>* test (new secret_impl_space::MyStruct<A>(aa)); return 0;}
However, I found that newer gcc versions, starting from 12 on, give me the following compilation error (confirmed with godbolt):
no match for ‘operator>>’ (operand types are ‘std::istream’ {aka ‘std::basic_istream<char>’} and ‘A’)
The weirdest thing is that the code compiles correctly on any gcc version if I do one of the following two things:
- Get rid of the namespace "secret_impl_space"
- Remove the virtual specifier from the read() method.
Can someone explain this behavior? I am honestly puzzled.
Note: just to give the readers some context, in the original code MyStruct was the implementation part of a type-erasing container like boost::any - that is why it has a virtual >> method, to overload the one in the type-erased base interface. The whole idea behind defining the Dummy class was to allow using the type-erased container also for some types that do not have a >> operator - generating a Runtime warning instead of a compiler error. This is pretty terrible IMHO, but I did not write this, it was already around when I found the problem. All this machinery was 'hidden', for some reason (shame?), inside a namespace.