У меня есть простая проблема с разрешением перегрузки ctor для шаблона класса:
#include <iostream>
#include <string>
using namespace std;
enum EnumTypeVal { READ, WRITE };
template <class T>
class TemplateClassTest {
public:
TemplateClassTest(const string & stringVal, T typeVal, EnumTypeVal e = READ,
const string & defaultStringVal = "default");
TemplateClassTest(const string & stringVal, const char * charVal);
TemplateClassTest(const string & stringVal, EnumTypeVal e = READ,
const string & defaultStringVal = "default");
private:
T type;
};
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, T typeVal,
EnumTypeVal e,
const string & defaultStringVal)
{
type = typeVal;
cout << "In TemplateClassTest(const string &, T, EnumTypeVal, ""const string &)" << endl;
}
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal,
const char * charVal)
{
cout << "In TemplateClassTest(const string &, const char *)" << endl;
}
template <class T>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, EnumTypeVal e,
const string & defaultStringVal)
{
cout << "In TemplateClassTest(const string &, EnumTypeVal, const string &)"<< endl;
}
typedef TemplateClassTest<long long unsigned int> u32Type;
typedef TemplateClassTest<bool> boolType;
int main()
{
u32Type l("test", "0"); //matches ctor 2
u32Type v("test", 0); // ambiguity between ctor 1 and 2
boolType b("test", "true"); //matches ctor 2
return 0;
}
Второй вызов не может быть скомпилирован из-за ошибки:
Вызов перегруженного ‘TemplateClassTest (const char [5], int) неоднозначен.
Почему int
соответствует const char *
? Эту ситуацию можно решить, изменив const char *
в const string &
в ctor 2. Но при этом, boolType b("test", "true")
теперь получает соответствие ctor 1 вместо ctor 2.
Мои требования:
u32Type v("test", 0)
должен соответствовать ctor 1boolType b("test", "true")
должен соответствовать ctor 2.Ограничения:
Любая помощь высоко ценится .. Спасибо!
Это неоднозначно, потому что 0
является константой нулевого указателя. Константа нулевого указателя может быть неявно преобразована в любой тип указателя -> a преобразование нулевого указателя. Следовательно, ctor 2 является жизнеспособным: TemplateClassTest(const string&, const char*)
,
Как целочисленный литерал, 0
имеет тип int
, Конструктор 1, TemplateClassTest(const string&, T, EnumTypeVal = READ, const string & = "default")
, следовательно, требуется преобразование из int
в unsigned long long
-> ан интегральное преобразование.
Эти два преобразования, преобразование нулевого указателя а также интегральное преобразование, имеют одинаковый ранг (конверсия), отсюда и неоднозначность.
Константа нулевого указателя — это целочисленное выражение константы prvalue целочисленного типа, которое оценивается как ноль или prvalue типа
std::nullptr_t
, Константа нулевого указателя может быть преобразована в тип указателя; результатом является значение нулевого указателя этого типа и отличается от любого другого значения указателя объекта или типа указателя на функцию. Такое преобразование называется преобразование нулевого указателя.
Возможное, но безобразное исправление, соответствующее вашим ограничениям, состоит в том, чтобы изменить второй конструктор на:
template<class U,
class V = typename std::enable_if<std::is_same<U, const char*>{}>::type>
TemplateClassTest(const string & stringVal, U charVal);
То есть жадный шаблон конструктора, ограниченный SFINAE только для принятия const char*
как второй аргумент. Это сильно ограничивает неявные преобразования, применяемые ко второму аргументу при попытке сопоставить этот ctor.
Внеочередное определение становится:
template <class T>
template<class U, class V>
TemplateClassTest<T>::TemplateClassTest(const string & stringVal, U charVal)
{
cout << "In TemplateClassTest(const string &, const char *)" << endl;
}
Других решений пока нет …