简体   繁体   English

ADL找不到模板化的免费功能

[英]ADL does not find templated free function

I tried to understand how ADL works, at least its basics, and created following piece of code: 我试图了解ADL的工作原理,至少是基础知识,并创建了以下代码:

#include <iostream>
#include <string>
#include <vector>  

using std::pair;
using std::string;
using std::vector;

namespace My {
    using std::to_string;

    /** With these forward declarations it works ...
    string to_string(char arg);
    const string& to_string(const string& str);
    template <typename T>
        string to_string(const vector<T>& rhs);
    template <typename T1, typename T2>
        string to_string(const pair<T1, T2>& rhs);
    struct A;
    string to_string(const A& rhs);
    */

    string to_string(char arg)
    {
        return string(1, arg);
    }

    const string& to_string(const string& str)
    {
        return str;
    }

    template <typename T>
    string to_string(const vector<T>& rhs)
    {
        string str("");
        for (const auto& e : rhs) {
            str += to_string(e) + " ";      //< this fails with `pair<..>`
            /// `(to_string)(e)` would fail even with `A`
        }
        return str;
    }

    template <typename T1, typename T2>
    string to_string(const pair<T1, T2>& rhs)
    {
        return to_string(rhs.first) + " " + to_string(rhs.second);
    }

    struct A {
        static int counter;
        string to_string() const
        {
            using My::to_string;    //< avoid using `A::to_string`
            return to_string("<A>[") + to_string(counter++) + to_string("]");
        }
    };
    int A::counter = 0;

    string to_string(const A& rhs)
    {
        return rhs.to_string();
    }
}

int main(int /*argc*/, const char* /*argv*/[])
{
    using My::to_string;
    using My::A;
    using std::cout;
    using std::endl;

    cout << to_string(3.1415) << endl;
    cout << to_string(pair<int, char>{5, 'a'}) << endl;
    cout << to_string(pair<double, pair<int, int>>{3.14, {1, 2}}) << endl;
    cout << to_string(vector<int>{1, 2, 3}) << endl;
    cout << to_string(pair<string, vector<int>>{"key", {1, 2, 3}}) << endl;
    cout << to_string(pair<string, A>{"key", {}}) << endl;
    cout << to_string(vector<A>{{}, {}, {}}) << endl;
    /// this will fail to compile
    cout << to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}}) << endl;

    return 0;
}

I figured out that, inside of My , using std::to_string will use std:: free function if it exists and will use My:: otherwise. 我发现在My里面, using std::to_string将使用std:: free函数(如果存在),否则将使用My:: Then, outside of namespace My , it is sufficient to do using My::to_string to cover both cases. 然后,在My命名空间之外, using My::to_string覆盖这两种情况就足够了。 Fine so far. 到目前为止很好。

Then I used the same technique inside member function A::to_string to avoid preferring the function itself instead of free functions (the same will apply for any other member function). 然后,我在成员函数A::to_string使用了相同的技术,以避免偏向于函数本身而不是自由函数(其他成员函数也是如此)。

Finally, I was a little surprised that to_string(vector<A>) compiles, although A is not forward-declared. 最后,尽管没有预先声明Ato_string(vector<A>)使我有些惊讶。 That's where ADL kicks in, as I've understood. 据我所知,这就是ADL发挥作用的地方。 Disabling it (enclosing to_string into brackets) will cause compilation failure. 禁用它(将to_string括在方括号中)将导致编译失败。

Now after the long story, here goes my question: why ADL does not work in this case for templated function, ie to_string(pair<T1, T2>) ? 现在,经过漫长的故事之后,我想到了一个问题:为什么在这种情况下ADL对于模板化函数(即to_string(pair<T1, T2>)不起作用? And more importantly, how to fix it? 更重要的是,如何解决? I would be glad if it was not necessary to perform forward declarations, as in my use case the definition of to_string(vector<T>) is located in some base header file and the latter definitions are in different headers and are not supposed to be known at this time. 如果不需要执行前向声明,我会很高兴,因为在我的用例中, to_string(vector<T>)的定义位于某些基本头文件中,而后者的定义在不同的头中,因此不应目前已知。

EDIT : 编辑

I tried to somehow "fake" needed forward declarations by some templates or even with some SFINAE, but it either led to ambiguities or to the same result. 我试图通过某些模板甚至使用某些SFINAE,以某种方式“伪造”所需的前向声明,但是这导致了歧义或相同的结果。 Finally, I came up with solution which uses member function to_string (but it could be any other name) of desired class. 最后,我想出了使用所需类的成员函数to_string (但可以是任何其他名称)的解决方案。 This requires to always implement this function if compatibility with to_string is desired, which in case of STL containers requires to inherit them and add the member function. 如果需要与to_string兼容,则这需要始终实现此功能,如果是STL容器,则需要继承它们并添加成员函数。 But I believe that in this case ADL will never fail. 但是我相信,在这种情况下,ADL将永远不会失败。

Here are modified parts of the code: 这是代码的修改部分:

template <typename T,
          typename Fun = decltype(&T::to_string),
          typename = std::enable_if_t<
              std::is_member_function_pointer<Fun>::value>>
string to_string(const T& rhs)
{
    return rhs.to_string();
}

template <typename T>
struct Vector : public vector<T> {
    using vector<T>::vector;

    string to_string() const
    {
        using My::to_string;    //< avoid using `Vector::to_string`
        string str("");
        for (const auto& e : *this) {
            str += to_string(e) + " ";
        }
        return str;
    }
};

template <typename T1, typename T2>
struct Pair : public pair<T1, T2> {
    using pair<T1, T2>::pair;

    string to_string() const
    {
        using My::to_string;    //< avoid using `Pair::to_string`
        return to_string(this->first) + " " + to_string(this->second);
    }
};

However, one has to replace vector s and pair s with Vector s and Pair s. 但是,必须用VectorPair替换vectorpair (Free function to_string(A) is not needed any more). (不再需要自由函数to_string(A) )。

Other solutions, comments? 其他解决方案,有何评论?

why ADL does not work in this case for templated function, ie to_string(pair<T1, T2>) ? 为什么ADL在这种情况下不适用于模板函数,即to_string(pair<T1, T2>)

ADL works by inspecting the namespaces associated with the types involved in a given call. ADL通过检查与给定调用中涉及的类型关联的名称空间来工作。 It then will consider the appropriate overloads found in those namespaces and select the best one. 然后,它将考虑在这些命名空间中找到的适当重载,并选择最佳的重载。

to_string(vector<pair<string, int>>{{"a", 1}, {"b", 2}})

This call will select the overload My::to_string(const vector<T>&) and this in turns calls to_string(std::pair<std::string, int>) . 该调用将选择重载My::to_string(const vector<T>&)然后to_string(std::pair<std::string, int>)调用to_string(std::pair<std::string, int>)

ADL inspect then the namespaces associated with std::pair , std::string and int to look for the overload to_string(std::pair<...>) . 然后,ADL检查与std::pairstd::stringint相关联的名称空间,以查找重载to_string(std::pair<...>) Since no such overload is defined in the namespace std it needs to find the definition previous to the call, but the overload My::to_string(const pair<T1, T2>&) is defined after. 由于在命名空间std没有定义这样的重载,因此需要在调用之前找到定义,但是重载My::to_string(const pair<T1, T2>&)是在之后定义的。 That's why you need to forward declare it. 这就是为什么您需要转发声明它的原因。

Notice that you need to forward declare it because you also have this: 注意,您需要转发声明它,因为您还具有以下内容:

to_string(pair<string, vector<int>>)

If, on the other hand, you had something like: 另一方面,如果您有类似以下内容:

to_string(vector<pair<string, My::A>>{{"a", {}}, {"b", {}}})

Then, one of the associated namespaces for the call will be My itself and the overload to_string(std::pair<...>) will be found without the need to forward declare it. 然后,该调用的关联名称空间之一将是My本身,并且无需转发声明即可找到重载to_string(std::pair<...>)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM