Я согласен с тем, что они, скорее всего, искали вариационные шаблоны , но ради этого существуют разные подходы, которые можно использовать в C ++ 03:
Использование варианта типа
Использовать контейнер варианта типа. В этом случае boost::variant
не будет работать, так как ограничивает количество типов, но вы можете использовать boost::any
:
void foo( std::vector< boost::any > args );
По сравнению с шаблонами с переменными числами код пользователя будет намного более громоздким, поскольку вместо записи foo( a, b, c, d )
им придется вручную создавать вектор заранее. Синтаксис можно упростить с помощью переменных макросов (если их поддерживает компилятор) и / или вспомогательных шаблонных функций для адаптации синтаксиса, но это довольно легко может привести к путанице.
C-путь (не шаблон):
Используйте многоточие для записи функции, которая принимает неизвестное количество аргументов (и типов):
void foo( type x, ... )
У этого подхода много недостатков. Во-первых, он не является безопасным с точки зрения типов, компилятор не сможет обнаружить, что аргументы являются правильным числом или типами, и это неопределенное поведение, если какой-либо из аргументов имеет тип не POD, что ограничивает удобство использования с любой тип для типов POD, который может быть или не быть ограничивающим фактором (вы всегда можете передать указатель на объект, не являющийся POD). В целом, это сложнее обрабатывать и гораздо более подвержено ошибкам, поэтому его следует избегать.
Не отвечая на вопрос вообще
В очень немногих ситуациях одна функция должна иметь возможность принимать неизвестное количество аргументов неизвестного типа. Ведение журнала и ввод-вывод может потребовать этого, printf
является таким примером. Но это может быть обработано в C ++ посредством перегрузки операторов (в частности, operator<<
) и цепочки. В комментарии было предложено bind
, так что да, идеальная пересылка в универсальном коде является одним из таких случаев, bind
, std::thread
...
Он считает, что это хороший ответ для интервью, поскольку вы можете обсудить, какова реальная потребность в функции и есть ли лучшая альтернатива. Можно утверждать, что если в конце вам действительно нужен контейнер варианта типа, вы можете злоупотреблять перегрузкой операторов, чтобы упростить синтаксис. Примером этого может служить библиотека boost::assign
, и в этих строках вы можете создать вспомогательный конструктор аргументов, например:
class args {
public:
args() {}
operator std::vector<boost::any>&() {
return v;
}
template <typename T>
args& operator,( T x ) {
boost::any a = x;
v.push_back( a );
return *this;
}
private:
std::vector<boost::any> v;
};
// usage:
void foo( std::vector<boost::any> a ) {
std::cout << "Received " << a.size() << " arguments" << std::endl;
}
int main() {
foo(( args(), 1, 5.0, "a string", std::vector<int>(5,10) ));
}
Вариативные шаблоны
И, конечно же, лучший вариант - это компилятор c ++ 0x, который обрабатывает шаблоны с переменным числом аргументов, не требующий дополнительного кода и значительно упрощает написание обоих пользовательских кодов (непосредственно как обычный вызов функции). и реализация функции, что бы это ни было. В качестве мотивирующего примера можно построить vector<boost::any>
с переменными аргументами:
typedef std::vector<boost::any> anyvector_t
// Stop condition, adding nothing at the end
void build_vector_impl( anyvector_t& ) {}
// Intermediate step, add a new argument to the vector and recurse:
template <typename Head, typename... Tail>
void build_vector_impl( anyvector_t& v, Head head, Tail... tail ) {
v.push_back( boost::any(head) );
build_vector_impl( v, tail... );
}
// Syntactic sugar: make it return the vector:
template <typename... Args>
anyvector_t build_vector( Args... args ) {
anyvector_t res;
build_vector_impl( res, args... );
return res;
}
// Test:
int main() {
std::cout << "Number of args: "
<< build_vector( 1, 5, "Hi", std::vector<int>( 5, 10 ) ).size()
<< std::endl;
}