Специальная спецификация шаблона перегрузки GCC 4.9

Я столкнулся с проблемой с gcc 4.9.2 (с -std = С++ 11), не компилируя фрагмент кода с сообщением об ошибке

вызов перегруженного 'InsertDataIntoInputMap (int &, boost:: shared_ptr &)' является неоднозначным

Код компилируется с msvc 2013

#include <iostream>
#include <map>
#include <boost/shared_ptr.hpp>

struct Proxy
{
    typedef std::map<int, int> InputDataMap;    
    int a;
};

template<class C, class D>
void InsertDataIntoInputMap(
    const typename C::InputDataMap::key_type& key,
    const D val)
{
    std::cout << "Not shared\n";
}

template<class C, class D>
void InsertDataIntoInputMap(
    const typename C::InputDataMap::key_type& key,
    const boost::shared_ptr<D> val)
{
    if (val)
    {
        std::cout << "Shared\n";
    }
}

int main() {
    int a;
    boost::shared_ptr<double> x(new double(4.5));

    InsertDataIntoInputMap<Proxy>(a, x);
}

в то время как следующее действительно компилируется как с gcc, так и с msvc:

#include <iostream>
#include <boost/shared_ptr.hpp>

template<class C, class D>
void InsertDataIntoInputMap(
    const C& key,
    const D val)
{
    std::cout << "Not shared\n";
}

template<class C, class D>
void InsertDataIntoInputMap(
    const C& key,
    const boost::shared_ptr<D> val)
{
    if (val)
    {
        std::cout << "Shared\n";
    }
}

int main() {
    int a = 0;
    boost::shared_ptr<double> x(new double(4.5));

    InsertDataIntoInputMap<int>(a, x);

    return 0;
}

Я бы подумал, что компилятор должен взять функцию с аргументом boost:: shared_ptr в обоих случаях?

Ответы

Ответ 1

Эта проблема может быть сведена к неточности в частичном упорядочении: пары, в которых не появляются шаблонные параметры, которые участвуют в вычете, все еще рассматриваются и компилируются. Этот вопрос был рассмотрен CWG # 455 и #885 также.

В вашем примере разрешение перегрузки не может отличить перегрузки. Следовательно, необходим частичный порядок. И частичное упорядочение будет пытаться выполнить вывод дважды, при этом тип параметра P будет typename C::InputDataMap::key_type.
Однако этот вывод обречен на провал, так как C появляется только в невыводимом контексте. То есть тип из обоих шаблонов (для этой конкретной пары) не является, по меньшей мере, таким же специализированным, как тип из другого другого шаблона, и это, в свою очередь, подразумевает, что ни один из шаблонов не является более специализированным, чем другой.

Как отмечено в @T.C., помогает разрешение CWG # 1391. Эта часть, в частности:

Измените 14.8.2.4 [temp.deduct.partial] пункт 4 следующим образом:

Каждый тип, назначенный выше из шаблона параметра и соответствующий тип из шаблона аргумента, используется как типы P и A. Если конкретный P не содержит шаблонных параметров, участвующих в выводе аргумента шаблона, то P не используется для определения порядка.

Теперь первая пара параметров полностью игнорируется в обоих направлениях (поскольку типы C определяются исключительно из списка явных аргументов), а вторая перегрузка оказывается более специализированной.

Ответ 2

Простой псевдоним может заставить этот код работать:

#include <iostream>
#include <map>
#include <boost/shared_ptr.hpp>

struct Proxy
{
    typedef std::map<int, int> InputDataMap;    
    int a;
};

template<class C, class D, class F = typename C::InputDataMap::key_type>
void InsertDataIntoInputMap(
    const F& key,
    const D val)
{
    std::cout << "Not shared\n";
}

template<class C, class D, class F = typename C::InputDataMap::key_type>
void InsertDataIntoInputMap(
    const F& key,
    const boost::shared_ptr<D> val)
{
    if (val)
    {
        std::cout << "Shared\n";
    }
}

int main() {
    int a;
    boost::shared_ptr<double> x(new double(4.5));

    InsertDataIntoInputMap<Proxy>(a, x);
}

Но имо. это не должно работать, потому что я думал, что проект говорит, что компилятор не будет рассматривать C:: InputDataMap - пространство имен в

class F = typename C::InputDataMap::key_type

и F будет не выводимым контекстом (например, key_type).