Вопрос прост: как реализовать функцию, принимающую переменное число аргументов (подобно шаблону с переменным числом аргументов), однако, где все аргументы имеют одинаковый тип, скажем, int.
Я думал о чем-то похожем на это;
void func(int... Arguments)
В качестве альтернативы не будет рекурсивного статического утверждения о работе типов?
Возможное решение — сделать тип параметра контейнером, который можно инициализировать с помощью списка инициализаторов фигурных скобок, например std::initializer_list<int>
или же std::vector<int>
. Например:
#include <iostream>
#include <initializer_list>
void func(std::initializer_list<int> a_args)
{
for (auto i: a_args) std::cout << i << '\n';
}
int main()
{
func({4, 7});
func({4, 7, 12, 14});
}
Вот версия, которая удаляет функцию из набора перегрузки, вместо предоставления static_assert. Это позволяет вам предоставлять другие перегрузки функции, которые могут использоваться, когда типы не одинаковы, вместо фатального static_assert, которого нельзя избежать.
#include <type_traits>
template<typename... T>
struct all_same : std::false_type { };
template<>
struct all_same<> : std::true_type { };
template<typename T>
struct all_same<T> : std::true_type { };
template<typename T, typename... Ts>
struct all_same<T, T, Ts...> : all_same<T, Ts...> { };
template<typename... T>
typename std::enable_if<all_same<T...>::value, void>::type
func(T...)
{ }
Если вы хотите поддерживать совершенную переадресацию, вы, вероятно, захотите уничтожить типы перед проверкой их, чтобы функция принимала сочетание аргументов lvalue и rvalue, если они имеют один и тот же тип:
template<typename... T>
typename std::enable_if<all_same<typename std::decay<T>::type...>::value, void>::type
func(T&&...)
{ }
В качестве альтернативы, если у вас есть универсальная черта для тестирования логического соединения, вы можете сделать это, используя std::is_same
вместо того, чтобы писать свой собственный all_same
:
template<typename T, typename... Ts>
typename std::enable_if<and_<is_same<T, Ts>...>::value, void>::type
func(T&&, Ts&&...)
{ }
Поскольку для этого требуется как минимум один аргумент, вам также потребуется другая перегрузка для поддержки случая с нулевым аргументом:
void func() { }
and_
хелпер можно определить так:
template<typename...>
struct and_;
template<>
struct and_<>
: public std::true_type
{ };
template<typename B1>
struct and_<B1>
: public B1
{ };
template<typename B1, typename B2>
struct and_<B1, B2>
: public std::conditional<B1::value, B2, B1>::type
{ };
template<typename B1, typename B2, typename B3, typename... Bn>
struct and_<B1, B2, B3, Bn...>
: public std::conditional<B1::value, and_<B2, B3, Bn...>, B1>::type
{ };
Я думаю, что вы можете сделать это, указав конкретный тип при жевании ваших аргументов из пакета аргументов. Что-то вроде:
class MyClass{};
class MyOtherClass{};
void func()
{
// do something
}
template< typename... Arguments >
void func( MyClass arg, Arguments ... args )
{
// do something with arg
func( args... );
// do something more with arg
}void main()
{
MyClass a, b, c;
MyOtherClass d;
int i;
float f;
func( a, b, c ); // compiles fine
func( i, f, d ); // cannot convert
}
В общем случае void func( MyClass arg, Arguments ... args )
станет void func( arg, Arguments ... args )
с шаблоном типа T.
@Skeen
Как насчет этого?
template <typename T>
void func_1(std::initializer_list<T>&& a) {
// do something
}
template <typename... T>
void func(T&&... a) {
func_1({std::forward<T>(a)...});
}
int main() {
func(1, 2, 3);
// func(1, 2, 3, 4.0); // OK doesn't compile
}
Если вы не хотите использовать скобки initializer_list
/vector
и хотите сохранить аргументы отдельно в виде пакета аргументов, а затем приведенное ниже решение проверяет его во время компиляции с использованием рекурсивного static_assert
s:
#include<type_traits>
template<typename T1, typename T2, typename... Error>
struct is_same : std::false_type {};
template<typename T, typename... Checking>
struct is_same<T, T, Checking...> : is_same<T, Checking...> {};
template<typename T>
struct is_same<T,T> : std::true_type {};
template<typename... LeftMost>
void func (LeftMost&&... args)
{
static_assert(is_same<typename std::decay<LeftMost>::type...>::value,
"All types are not same as 'LeftMost'");
// ...
}
int main ()
{
int var = 2;
func(1,var,3,4,5); // ok
func(1,2,3,4.0,5); // error due to `static_assert` failure
}
На самом деле это решение будет проверять все аргументы относительно первого аргумента. Предположим, это было double
тогда все будет проверено double
,
Поскольку я не думаю, что видел это решение, вы могли бы написать определенную функцию для каждого типа (в вашем случае, просто int
) затем функция пересылки, принимающая переменные типы аргументов.
Напишите каждый конкретный случай:
тогда для каждого конкретного случая:
// only int in your case
void func(int i){
std::cout << "int i = " << i << std::endl;
}
Тогда ваша функция пересылки выглядит так:
template<typename Arg0, typename Arg1 typename ... Args>
void func(Arg0 &&arg0, Arg1 &&arg1, Args &&... args){
func(std::forward<Arg0>(arg0));
func(std::forward<Arg1>(arg1), std::forward<Args>(args)...);
}
Это хорошо, потому что его можно расширять, если вы хотите принять, возможно, другой тип.
Используется так:
int main(){
func(1, 2, 3, 4); // works fine
func(1.0f, 2.0f, 3.0f, 4.0f); // compile error, no func(float)
}