peruch
peruch

Reputation: 33

C++ weird templates/namespaces behavior

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:

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.

Upvotes: 3

Views: 202

Answers (2)

Coral Kashri
Coral Kashri

Reputation: 3506

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.

Upvotes: 0

Jarod42
Jarod42

Reputation: 217085

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

Upvotes: 1

Related Questions