Почему бы не вывести параметр шаблона из конструктора?

StackOverflow https://stackoverflow.com/questions/984394

Вопрос

Мой вопрос сегодня довольно прост: почему компилятор не может вывести параметры шаблона из конструкторов класса, как это может сделать из параметров функции? Например, почему следующий код не может быть действительным:

template<typename obj>
class Variable {
      obj data;
      public: Variable(obj d)
              {
                   data = d;
              }
};

int main()
{
    int num = 2;
    Variable var(num); //would be equivalent to Variable<int> var(num),
    return 0;          //but actually a compile error
}

Как я уже сказал, я понимаю, что это не так, поэтому мой вопрос Почему не так ли? Позволяет ли это создать какие -либо серьезные синтаксические отверстия? Есть ли экземпляр, в котором не хотелось бы этой функции (где вывод типа вызовет проблемы)? Я просто пытаюсь понять логику, стоящую за разрешением вывода шаблона для функций, но не для соответствующих классов.

Это было полезно?

Решение

Я думаю, что это недопустимо, потому что конструктор не всегда единственная точка ввода класса (я говорю о конструкторе копирования и операторе =). Так что предположим, что вы используете свой класс, как это:

MyClass m(string s);
MyClass *pm;
*pm = m;

Я не уверен, было бы так очевидно, что анализатор будет знать, какой тип шаблона является PM MyClass;

Не уверен, что то, что я сказал, имеет смысл, но не стесняйтесь добавлять некоторые комментарии, это интересный вопрос.

C ++ 17

Принято, что C ++ 17 будет иметь вычет типа из аргументов конструктора.

Примеры:

std::pair p(2, 4.5);
std::tuple t(4, 3, 2.5);

Принятая бумага.

Другие советы

Вы не можете сделать то, что вы спрашиваете по причинам, которые обратились другие люди, но вы можете сделать это:

template<typename T>
class Variable {
    public: Variable(T d) {}
};
template<typename T>
Variable<T> make_variable(T instance) {
  return Variable<T>(instance);
}

Что для всех намерений и целей - это то же самое, что вы просите. Если вы любите инкапсуляцию, вы можете сделать Make_variable функцией статического члена. Это то, что люди называют названным конструктором. Таким образом, он не только делает то, что вы хотите, но и почти так называют то, что вы хотите: компилятор предполагает параметр шаблона из (именованного) конструктора.

NB: любой разумный компилятор оптимизирует временный объект, когда вы напишите что -то вроде

Variable<T> v = make_variable(instance);

В просвещенном возрасте 2016 года, с двумя новыми стандартами под нашим поясом с момента задания этого вопроса, и новым за горами, что важно знать, что это то, что Компиляторы, поддерживающие стандарт C ++ 17 Скомпилируйте свой код как есть.

Шаблон-аргумент вычет для шаблонов класса в C ++ 17

Здесь (Предоставлено редактированием Ольцхаса Жхумабека об принятом ответе) - это документ с подробным описанием соответствующих изменений в стандарте.

Обращение к проблемам со стороны других ответов

Текущий ответ с высоким рейтингом

Этот ответ указывает на то, что «Конструктор копий и operator="Не знал бы правильные специализации шаблона.

Это чепуха, потому что стандартный конструктор копирования и operator= существует только для известен Тип шаблона:

template <typename T>
class MyClass {
    MyClass(const MyClass&) =default;
    ... etc...
};

// usage example modified from the answer
MyClass m(string("blah blah blah"));
MyClass *pm;   // WHAT IS THIS?
*pm = m;

Здесь, как я отметил в комментариях, есть нет причин за MyClass *pm быть юридическим декларацией с новой формой вывода или без него: MyClass не является типом (Это шаблон), поэтому не имеет смысла объявлять указатель типа MyClass. Анкет Вот один из возможных способов исправить пример:

MyClass m(string("blah blah blah"));
decltype(m) *pm;               // uses type inference!
*pm = m;

Здесь, pm является уже правильного типа, и поэтому вывод тривиальный. Более того, невозможно случайно смешивание Типы при вызове конструктора копирования:

MyClass m(string("blah blah blah"));
auto pm = &(MyClass(m));

Здесь, pm будет указатель на копию m. Анкет Здесь, MyClass является созданным копией из m- это тип MyClass<string> (а также нет несуществующего типа MyClass) Таким образом, в тот момент, где pmТип выведен, там является достаточная информация, чтобы узнать, что тип шаблона m, и, следовательно, тип шаблона pm, является string.

Более того, следующая завещание всегда Установите ошибку компиляции:

MyClass s(string("blah blah blah"));
MyClass i(3);
i = s;

Это потому, что объявление конструктора копирования нет шаблон:

MyClass(const MyClass&);

Здесь шаблонный тип аргумента Спички Шаблонный тип класса в целом; т.е. когда MyClass<string> экземпляры, MyClass<string>::MyClass(const MyClass<string>&); создан с ним и когда MyClass<int> экземпляры, MyClass<int>::MyClass(const MyClass<int>&); экземпляр. Если это не указано явно или не будет объявлен омплатированный конструктор, нет причин для компилятора экземпляры MyClass<int>::MyClass(const MyClass<string>&);, что, очевидно, было бы неуместным.

Ответ Cătălin Pitiș

PITIș приводит пример, выводящий Variable<int> а также Variable<double>, затем утверждает:

У меня одинаковое имя типа (переменная) в коде для двух разных типов (переменная и переменная). С моей субъективной точки зрения это в значительной степени влияет на читаемость кода.

Как отмечено в предыдущем примере, Variable сам есть нет Имя типа, хотя новая функция делает его похожим на синтаксически.

Тогда Pitiș спрашивает, что произойдет, если конструктор не будет предоставлен, который позволит соответствующему выводу. Ответ заключается в том, что вывод не допускается, потому что вывод запускается конструктор вызов. Анкет Без конструктора Нет вывода.

Это похоже на вопрос, какая версия foo выведен здесь:

template <typename T> foo();
foo();

Ответ заключается в том, что этот код является незаконным, по указанной причине.

Ответ MSALTER

Насколько я могу судить, это единственный ответ, чтобы вызвать законную обеспокоенность по поводу предлагаемой функции.

Пример:

Variable var(num);  // If equivalent to Variable<int> var(num),
Variable var2(var); // Variable<int> or Variable<Variable<int>> ?

Ключевой вопрос: выбирает ли компилятор тип-информированный конструктор здесь или копия конструктор?

Попробуя код, мы видим, что выбран конструктор копирования. Чтобы расширить пример:

Variable var(num);          // infering ctor
Variable var2(var);         // copy ctor
Variable var3(move(var));   // move ctor
// Variable var4(Variable(num));     // compiler error

Я не уверен, как предложение и новая версия стандарта указывают это; Похоже, что это определяется «руководствами для вывода», которые являются новой частью стандарта, которых я еще не понимаю.

Я также не уверен, почему var4 Вычет является незаконным; Ошибка компилятора от G ++, по -видимому, указывает на то, что оператор проанализируется как объявление функции.

Все еще отсутствует: он делает следующий код довольно неоднозначным:

int main()
{
    int num = 2;
    Variable var(num);  // If equivalent to Variable<int> var(num),
    Variable var2(var); //Variable<int> or Variable<Variable<int>> ?
}

Предположим, что компилятор поддерживает то, что вы просили. Тогда этот код действителен:

Variable v1( 10); // Variable<int>

// Some code here

Variable v2( 20.4); // Variable<double>

Теперь у меня есть одинаковое имя типа (переменная) в коде для двух разных типов (переменная и переменная). С моей субъективной точки зрения это в значительной степени влияет на читаемость кода. Наличие одного имени типа для двух разных типов в одном и том же пространстве имен выглядит вводящим в заблуждение для меня.

Позже обновление:Еще одна вещь, которую следует рассмотреть: частичная (или полная) специализация шаблона.

Что если я специализирую переменную и не предоставляю конструктора, как вы ожидаете?

Так что я бы имел:

template<>
class Variable<int>
{
// Provide default constructor only.
};

Тогда у меня есть код:

Variable v( 10);

Что должен делать компилятор? Используйте общее определение класса переменной, чтобы сделать вывод, что он является переменной, затем обнаружите, что переменная не предоставляет один конструктор параметров?

Стандарт C ++ 03 и C ++ 11 не допускает вычета аргумента шаблона из параметров, передаваемых константуратору.

Но есть предложение о «вычете параметров шаблона для конструкторов», чтобы вы могли получить то, о чем просите скоро. РЕДАКТИРОВАТЬ: Действительно, эта функция была подтверждена для C ++ 17.

Видеть: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3602.html а также http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0091r0.html

Многие классы не зависят от параметров конструктора. Есть только несколько классов, в которых есть только один конструктор, и параметризуется на основе типа (ы) этого конструктора.

Если вам действительно нужен шаблонный вывод, используйте вспомогательную функцию:

template<typename obj>
class Variable 
{
      obj data;
public: 
      Variable(obj d)
      : data(d)
      { }
};

template<typename obj>
inline Variable<obj> makeVariable(const obj& d)
{
    return Variable<obj>(d);
}

Вычет типов ограничена функциями шаблона в текущем C ++, но долгое время осознавалось, что вычет типа в других контекстах был бы очень полезным. Отсюда C ++ 0x's auto.

Пока в яблочко То, что вы предлагаете, не будет возможно в C ++ 0x, следующее показывает, что вы можете получить довольно близко:

template <class X>
Variable<typename std::remove_reference<X>::type> MakeVariable(X&& x)
{
    // remove reference required for the case that x is an lvalue
    return Variable<typename std::remove_reference<X>::type>(std::forward(x));
}

void test()
{
    auto v = MakeVariable(2); // v is of type Variable<int>
}

Вы правы, компилятор мог бы легко догадаться, но, насколько я знаю, не в стандарте или C ++ 0x, поэтому вам придется подождать, по крайней мере, 10 лет (стандартные стандарты ISO фиксированный поворот вокруг), прежде чем компиляторы добавят эту функцию

Давайте посмотрим на проблему со ссылкой на класс, который каждый должен быть знаком с - Std :: Vector.

Во -первых, очень распространенное использование вектора - использовать конструктор, который не принимает параметров:

vector <int> v;

В этом случае, очевидно, не может быть сделан вывод.

Второе распространенное использование-создать предварительный вектор:

vector <string> v(100);

Здесь использовался вывод:

vector v(100);

Мы получаем вектор INT, а не струны, и, по -видимому, это не размер!

Наконец, рассмотрим конструкторы, которые принимают несколько параметров - с «выводом»:

vector v( 100, foobar() );      // foobar is some class

Какой параметр следует использовать для вывода? Нам понадобится какой -нибудь способ сказать компилятору, что это должно быть вторым.

Со всеми этими проблемами для класса, столь же простых, как вектор, легко понять, почему вывод не используется.

Сделать шаблон CTOR, переменная может иметь только один форма Но различные CTOR:

class Variable {
      obj data; // let the compiler guess
      public:
      template<typename obj>
      Variable(obj d)
       {
           data = d;
       }
};

int main()
{
    int num = 2;
    Variable var(num);  // Variable::data int?

    float num2 = 2.0f;
    Variable var2(num2);  // Variable::data float?
    return 0;         
}

Видеть? У нас не может быть несколько элементов переменной :: Data.

Видеть Вычет аргумента шаблона C ++ Для получения дополнительной информации об этом.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top