Как использовать 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