Почему передача такого параметра переменного размера по значению невозможна?

У нас есть структура, как показано ниже:

template<size_t size>
class Container{
 public:
  char characters[size];
};

И у нас есть функция, как показано ниже:

template <size_t size>
void function(Container<size> input,size_t Size){
 //all instances do exactly same thing and with regard to Size that determines the size of object
}

Теперь в С++ для каждого значения размера будет создан другой экземпляр функции, и, по-видимому, в данном случае это неправильно, поскольку все экземпляры делают одно и то же, чтобы избежать этого, первый параметр функции должен быть указателем (char * указатель) вместо этого объекта, который принимает любой массив любого размера, что устраняет необходимость в шаблоне функции, но мне любопытно иметь единственную функцию, которая принимает параметр переменного размера, как указано выше, который не разрешен С++, что-то вообще невозможно реализовать и генерировать сборку для, или как-то приводит к неэффективной реализации с точки зрения скорости/памяти?


person Pooria    schedule 19.11.2010    source источник
comment
Какой смысл передавать size вашей функции? Container<size> уже имеет эту информацию.   -  person ereOn    schedule 19.11.2010
comment
Любые изменения в OP, пожалуйста, укажите как EDIT 2 или что-то в этом роде, чтобы ответы, данные до редактирования, не выглядели неправильно.   -  person Chubsdad    schedule 19.11.2010
comment
Что вас так беспокоит в наличии нескольких функций, которые делают одно и то же... до тех пор, пока вы пишете только ОДНУ МОДЕЛЬ для всех них? Знаете, это цель шаблонов.   -  person Matthieu M.    schedule 19.11.2010


Ответы (6)


Как правило, если вам нужен параметр IN, передавайте встроенные типы по значению, а другие типы по ссылке на const, т. е.

template< size_t size >
void function( Container<size> const& input,size_t Size )
{
 //all instances do exactly same thing and with regard to Size that determines the size of object
}

С этим определением есть вероятность, что компилятор + компоновщик оптимизируют все так, что будет только одна версия машинного кода function.

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

Попробуйте сами, и если вы так же удивлены, как когда-то был я, то хорошо! В противном случае, скорее всего, не будет существенной разницы. Но в некоторых крайних случаях вы можете обнаружить то, что в старые времена называлось «раздуванием шаблонного кода», и тогда пришло время игнорировать это или измерить, достаточно ли это важно, чтобы выполнить работу по переводу полиморфизма времени выполнения.

Теперь на ваш вопрос,

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

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

Безопасный способ — использовать std::string из стандартной библиотеки C++, заголовок <string>. Это включает в себя динамическое выделение где-то внутри std::string, которое выполняется автоматически для вас, но влияет на эффективность. Но ваш код, содержащий char[size] characters, не был допустимым C++, и это указывает на начальный уровень, поэтому есть вероятность, что ваш дизайн не был выбран по какой-либо веской причине, поэтому сделайте:

class Container
{
public:
    std::string characters;
};

void function( Container const& input )
{
    // whatever, using e.g. input.characters.length()
}

Ура и чт.,

person Cheers and hth. - Alf    schedule 19.11.2010
comment
@Alf P.Steinbach_С этим определением есть вероятность, что компилятор + компоновщик оптимизирует вещи так, что будет только одна версия машинного кода функции, которую я думал, иметь одну версию машинного кода, когда есть разные аргументы шаблона, невозможно. - person Pooria; 19.11.2010
comment
@Pooria: о нет, не невозможно. тривиальный случай — когда различные экземпляры шаблона компилируются непосредственно в один и тот же машинный код. все, что требуется, - это распознать это компилятором + компоновщиком. - person Cheers and hth. - Alf; 19.11.2010
comment
@Alf P.Steinbach_Итак, вы имеете в виду, что, поскольку вы использовали ссылку в своем объявлении, вы сделали возможной одну версию машинного кода или каким-то образом передача по значению также может позволить это? - person Pooria; 19.11.2010
comment
@Pooria: здесь возможно слишком сильно, потому что это не было бы невозможно с исходным (исправленным для синтаксиса) кодом. Но гораздо более вероятно, да. Потому что со ссылочным аргументом то, что передается на уровне машинного кода, при условии отсутствия встраивания функции, на практике является просто адресом фактического аргумента. Ваше здоровье, - person Cheers and hth. - Alf; 19.11.2010
comment
@Alf P.Steinbach_ возможно здесь слишком силен, потому что это не было бы невозможно с исходным (исправленным для синтаксиса) кодом, под оригиналом вы имеете в виду мое объявление функции или ваше, которое имеет ссылочный параметр? - person Pooria; 19.11.2010
comment
@Pooria: под оригиналом я имел в виду объявление вашей функции с передачей по значению. передача по значению означает разные размеры аргумента для разных экземпляров шаблона функции, следовательно, возможно, другой машинный код. синтаксис, о котором я говорил не в функции, а в Container. Ваше здоровье, - person Cheers and hth. - Alf; 19.11.2010
comment
Я считаю, что для обычных вызовов в одном TU, если вы вызываете шаблон функции, а компилятор видит, что все вызовы в TU являются прямыми, и ни один из них не проходит через указатели функций, компилятор может вообще пропустить создание кода этой функции или объединить их в один, потому что в любой другой TU вызов функции, в свою очередь, создаст экземпляр функции и предоставит необходимый код: TU не зависят друг от друга таким образом. Это также означает, что TU, вызывающая шаблон функции, не может полагаться на определения, неявно созданные другими TU, — она может полагаться только на явные экземпляры других TU. - person Johannes Schaub - litb; 19.11.2010
comment
Поэтому я думаю, что @Alf здесь хороший момент: компоновщику не нужно быть умным, потому что вся работа выполняется компилятором и предоставляется спецификацией C++ (см. 14.8 в '03, который должен быть восстановлен с помощью open-std.org/jtc1/sc22/wg21/docs/cwg_active .html#1096 ) - person Johannes Schaub - litb; 19.11.2010

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

class ContainerBase {
 private: // can't be copied
  ContainerBase(ContainerBase const& o);
  ContainerBase &operator=(ContainerBase &);

 protected:
  ContainerBase(char *datap):datap(datap) { }

 public:
  char *data() { return datap; }

 private:
  char *datap;
};

template<std::size_t size>
class Container : public ContainerBase {
 public:
  Container():ContainerBase(d.characters)
  Container(Container const& o):ContainerBase(d.characters), d(o.d) 
  { }

  Container &operator=(Container const& o) {
    d = o.d;
    return *this;
  }

  struct {
   char characters[size];
  } d;
};


void function(ContainerBase const& input, std::size_t Size){
 /* operate on input.data() */
}

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

template<std::size_t size>
class Container : public ContainerBase {
 public:
  char *data() { return characters; }

 public:
  char characters[size];
};

// note that this function works in plain arrays too
void function(char *input, std::size_t Size){
 /* operate on input */
}

function(c.data(), N);

Между прочим, тогда вы находитесь в boost::array<char, N>, который предоставляет именно этот интерфейс и другие полезные функции (по-прежнему являющиеся агрегатом), хотя он слишком общий, поскольку не закреплен на char.

person Johannes Schaub - litb    schedule 19.11.2010
comment
_ Как вы думаете, как Альф П. Штейнбах упомянул, что одна версия машинного кода будет возможна для разных аргументов шаблона, если да, то какие могут быть эти ситуации, потому что я не могу заставить себя поверить в это. - person Pooria; 19.11.2010
comment
@Pooria достаточно умный компилятор/компоновщик может сделать это почти для всех случаев использования шаблонов функций. Я полагаю, что для обычных вызовов в одной TU, если вы вызываете шаблон функции, а компилятор видит, что все вызовы в TU являются прямыми и ни один из них не проходит через указатели функций, компилятор может пропустить создание кода этой функции. вообще, потому что в любой другой TU вызов функции, в свою очередь, создаст экземпляр функции и предоставит необходимый код: TU не зависят друг от друга таким образом. - person Johannes Schaub - litb; 19.11.2010
comment
Только когда вы явно сравниваете адреса функций, такие оптимизации больше невозможны. - person Johannes Schaub - litb; 19.11.2010
comment
_Так что, если я правильно понял, одна версия машинного кода может быть возможна для вызовов с разными аргументами шаблона в одной и только одной TU и только в том случае, если явные указатели функций на разные версии не используются, потому что, возможно, по стандарту эти указатели функций разных типов имеют указывать на разные версии функции, я прав? - person Pooria; 19.11.2010
comment
@Pooria, @Johannes: сравнение указателей функций не запрещает одну функцию машинного кода, но это проблема качества реализации (как и все эти вещи оптимизации): если есть сравнение указателей, то компилятор + компоновщик, возможно, придется генерировать несколько точек входа для функция. Здесь используется термин «точка входа» в значении SESE/SEME. Это сводится к n инструкциям перехода, которые переходят к началу реальной функции для n логических экземпляров функции. Ура и чт., - person Cheers and hth. - Alf; 20.11.2010

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

#include <cstddef>
#include <cstdio>

template <size_t size>
struct Container
{
    char characters[size];
};

template <size_t size>
inline void function(const Container<size>& input)
{
    real_work(input.characters, size);
}

void real_work(const char* p, size_t len)
{
    while (len--) puts(p++);
}

int main()
{
    Container<6> x = {"hello"};
    function(x);
}

Обратите внимание, что я намеренно назвал разные типы аргументов по-разному, чтобы вы не запутались.

person fredoverflow    schedule 19.11.2010
comment
@FredOverflow_Серьезно, зачем эта функция, если Container‹size›.characters можно передать напрямую в real_work? - person Pooria; 19.11.2010
comment
@Pooria: это упрощает интерфейс для пользователя. Если вы никогда не вызываете real_work напрямую (это может быть скрыто от пользователя), вы никогда не совершите ошибку передачи неверного значения для len. - person visitor; 19.11.2010

Контейнер‹1> — это другой тип, чем Контейнер‹2>, и передача указателя ничего вам не решит, потому что они будут указателями на разные типы.

Вам не кажется, что size должен быть параметром конструктора контейнера, а не шаблонным значением?

person Simone    schedule 19.11.2010
comment
тогда бы работало, но код такой, что у меня брови поднялись бы - person Simone; 19.11.2010

Все-таки плохой дизайн. Передача массивов по значению или структур или классов в этом отношении крайне неэффективна как во времени, так и в пространстве, поскольку весь массив должен быть скопирован в целевой фрейм стека. Передать ссылку.

person user207421    schedule 19.11.2010
comment
что, очевидно, требует реальных аргументов и показателей производительности ... что, если нам нужна семантика копирования? что, если мы говорим о небольших массивах из нескольких символов? что, если даже с двадцатью символами это все еще почти так же быстро? что, если мы действительно не заботимся о производительности здесь? Дизайн не должен зависеть от производительности, если в любом случае нет проблем с производительностью. - person Matthieu M.; 19.11.2010
comment
Я согласен с «показателями эффективности», но возражаю против «реальных аргументов». Вы не можете утверждать, что то, что я написал, не является реальным аргументом. Если вам нужна семантика копирования, конечно, вы должны заплатить цену. - person user207421; 19.11.2010

Будет ли что-то вроде этого приемлемым для вас:

class BaseContainer{ /* ... */ };

template<size_t size>
class Container : public BaseContainer{
 public:
  char[size] characters;
};

void function(BaseContainer input,size_t Size){
  //
}
person wimh    schedule 19.11.2010
comment
Это char[size] characters мне кажется синтаксической ошибкой... ;-) - person Cheers and hth. - Alf; 19.11.2010
comment
@Wimmel_Как вы думаете, можно ли написать тело этой функции?! - person Pooria; 19.11.2010
comment
char[size] действительно является синтаксической ошибкой. Я просто скопировал код сверху. Я просто хотел указать на наследование, чтобы решить эту проблему. И указатель или ссылка были бы лучше. @Pooria, я точно не знаю, чего ты хочешь. - person wimh; 19.11.2010
comment
@Wimmel_Вы не можете использовать ввод BaseContainer в этой функции, потому что, кроме size_t Size, в эту функцию вообще ничего не передается !!! - person Pooria; 19.11.2010
comment
Действительно, надо подумать. Как теперь function получит доступ к characters участникам? - person visitor; 19.11.2010