Как использовать std:: enable_if для условного выбора вариационного конструктора?
Я пытаюсь создать класс, который должен наследовать конструкторы из других классов, но не наследуя от самих этих классов.
В какой-то момент во время инициализации моего класса я хочу использовать совершенную пересылку для создания объекта типа, конструктор которого соответствовал заданным аргументам.
За исключением конструктора по умолчанию без аргументов, не должно быть двусмысленностей.
Это мой код:
#include <string>
using namespace std;
//NOTE: this class is just an example to demonstrate the problem
class String {
public:
//default constructor to prevent ambiguity
String() {}
//construct from wstring
template<typename... Args>
String(enable_if<is_constructible<wstring, Args...>::value, Args>::type&&... args) : ws(forward<Args>(args)...) {}
//construct from string
template<typename... Args>
String(enable_if<is_constructible<string, Args...>::value, Args>::type&&... args) : s(forward<Args>(args)...) {}
private:
string s;
wstring ws;
};
void foo(const String& string) {
}
int main()
{
foo(L"123");
foo("123");
return 0;
}
Я пробовал много вещей, но я просто не могу заставить его работать.
- В текущем подходе
enable_if
не удается автоматически вычесть шаблонные аргументы (я думаю)
- Поскольку я использую конструктор, я не могу использовать
enable_if
для возвращаемого значения
- Добавление другого параметра по умолчанию для
enable_if
не будет работать, потому что конструктор является переменным
- Когда я удаляю
enable_if
из аргументов функции, компилятор жалуется на недопустимые перегрузки (конечно)
Есть ли элегантный способ решить эту проблему?
Изменить:
Одно неявное преобразование, разрешенное стандартом, не должно происходить в моем классе. [отредактированный код кода]
Одним из решений, которое работает с вышеприведенным примером, было бы определение одного вариационного конструктора и идеальное перенаправление аргументов в функцию условной инициализации. Тем не менее, я хотел бы избежать этих накладных расходов, потому что члены должны быть сконфигурированы по умолчанию, и это может не работать в других случаях.
(Не стесняйтесь редактировать вопрос, если вещи могут быть понятнее)
Ответы
Ответ 1
Я не могу понять проблему, а также решение. Если я хочу использовать 2 разных типа для метода или конструктора, я могу просто написать оба типа. По этой причине нет необходимости иметь шаблон с SFINAE! Это можно сделать только по специализации.
class A
{
public:
template <typename ... Args>
A(const wstring &, Args ... );
template <typename ... Args>
A(const string &, Args ...);
};
Чтобы иметь шаблон, который точно соответствует только одному типу, на самом деле семантически не является шаблоном: -)
После прочтения вашего комментария я получил это решение:
class foo
{
public:
template <typename ... Args>
foo( const string &&x, Args ... ) { cout << "Using string" << endl; }
template <typename ... Args>
foo( const wstring &&x, Args ...) { cout << "Using wstring" << endl; }
};
int main()
{
foo("123");
foo(L"123");
foo("123", 1);
foo(L"123", 1.11);
return 0;
}
и это возвращается как ожидалось:
Using string
Using wstring
Using string
Using wstring