Специальная спецификация шаблона перегрузки 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).