3

I had a problem compiling my code since it wasn't able to find a matching function on a template. I've narrowed down the problem to this example:

namespace cv
{
    class FileNode
    { };

    template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
    {
        read(n, value, _Tp());
    }

    static inline void read(const FileNode& node, bool& value, bool default_value)
    { }
}

class K
{   };

namespace D
{
    class A
    { };
}

template<class X>
static void read(const cv::FileNode& node, X& x, const X& default_value)
{
    return;
}

using namespace D;
class B
{
    void read(const cv::FileNode& fn)
    {
        A a;
        fn >> a;
    }
};

int main(int argc, char* argv[]) { }

On Gcc 9.10 I get the following error:

invalid initialization of reference of type 'bool&' from expression of type 'D::A'  { read(n, value, _Tp()); }

On Visual Studio 2019:

Error   C2664    'void cv::read(const cv::FileNode &,bool &,bool)': cannot convert argument 2 from '_Tp' to 'bool &'

I've found any of the following changes will make the code compiling:

  • class A -> class A : public K
  • Delete read specialization for bool
  • Remove cv namespace
  • Move the read template inside namespace D

Unfortunately, none of the previous fixes is applicable to my original problem and I still don't really have an actual understanding on why exactly it's not able to find the read template.

peruch
  • 33
  • 4
  • Changing the declaration order might help: https://godbolt.org/z/ibe9DI – Kerrek SB Sep 13 '19 at 02:34
  • Interesting, that change fixes GCC 9.10 but not Visual Studio. – peruch Sep 13 '19 at 02:49
  • 1
    Why do you think the `read` template **should** be found? It appears after the caller and is not in the namespace of any associated type for the call. – Davis Herring Sep 13 '19 at 06:19
  • It's able to find it if we remove `read(const FileNode& node, bool& value, bool default_value)` so I would expect it to work also when that function is present – peruch Sep 13 '19 at 08:29
  • Identifiers starting with an underscore and an uppercase letter, like `_Tp`, are reserved for the standard library and forbidden for user code. – Erlkoenig Sep 14 '19 at 07:24

2 Answers2

1

ADL strikes back:

in

template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)
{
    read(n, value, _Tp());
}

read is undeclared, so can only be found by ADL

So it will search in namespace associated to FileNode (so cv), and the ones associated to _Tp.

when _Tp is D::A, it would be namespace D.

and the only possible overload of read is cv::read which take bool.

Moving declaration of read<T> above cv::operator >> solve the issue at it would also be considered with ADL.

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
0

Normally, I would separate the classes and the namespaces to different .hpp files, which automatically, will force you to make the function:

template<typename _Tp> static inline void operator >> (const FileNode& n, _Tp& value)

To be declared before the namespaces which use it (like cv).

I think that the best way to avoid this kind of problems is to clean your code, and to make it as separate and independent as you can. This way, whenever you want to use this unique read function in another namespaces, you won't need to put the new namespaces inside this file, or alternative include all the namespaces that are currently in this file.

Coral Kashri
  • 3,436
  • 2
  • 10
  • 22