具有可变数量的特定类型参数的 C++ 函数
C++ function with a viariable number of arguments of a certain type
我刚刚了解了 C++ 中的可变参数模板。我实现了,但我想知道,它能做到以下几点吗?
如果我想要一个参数数量可变的函数,我可以这样做:
template <typename... Ts>
f(Ts... args);
但是我失去了类型安全(我不知道参数的类型)。
如果我知道我的函数只需要 float
作为参数怎么办?我想在编译时确保每个参数都是我想要的类型。
所以这些是我的问题:
有没有办法强制使用可变参数模板(像这样)的特定类型?
template <float... Fs>
f(Fs... args); // unlimited number of arguments but only float
如果不是,有没有办法在编译时检查它? static_assert(std::is_same<A,B>)
在大多数情况下都很好,但它不适用于模板化的 类 (比如我的用例):
template <typename T, uint16_t dimension>
class Vector
{
template <typename... Ts>
Vector(Ts... args)
{
static_assert(sizeof...(args) == dimension);
static_assert(std::is_same_v<Ts..., T>()); //doesn't work because Ts will
//develop into a lot of template
//arguments. Just putting Ts doesn't
//work either.
}
}
Ps:是的,我可以使用 std::vector
或 std::array
作为参数,但这不是重点。另外,我想保留漂亮的 Vector(2.0, 1.0, 0.0)
语法,而不是使用大括号。
如果编译器支持C++ 20那么你可以这样写
#include <type_traits>
template <typename T, uint16_t dimension>
class Vector
{
public:
template <typename... Ts>
Vector( Ts &&... args ) requires ( sizeof...( args ) == dimension ) && std::conjunction_v<std::is_same<T, std::decay_t<Ts>>...>
{
}
};
//...
Vector<float, 5> v( 1.1f, 2.2f, 3.3f, 4.4f, 5.5f );
或者如@HolyBlackCat在评论中所写,您也可以写
template <typename T, uint16_t dimension>
class Vector
{
public:
template <typename... Ts>
Vector( std::same_as<T> auto ...args ) requires( sizeof...( args ) == dimension )
{
}
};
template <typename T, uint16_t dimension>
class MyVector
{
public:
template <typename... Ts>
MyVector(Ts... args)
{
static_assert(sizeof...(args) == dimension);
static_assert((std::is_same<T,Ts>::value && ...) );
}
};
也可以使用C++17提供的一元右折叠表达式
https://en.cppreference.com/w/cpp/language/fold
这基本上是
std::is_same<T,Ts_1>::value && ( std::is_same<T,Ts_2>::value && ... std::is_same<T,Ts_n>::value ) ...)
并且在参数包 Ts 为空时也有效。引自参考 link:
" Logical AND (&&). The value for the empty pack is true"
对于固定矢量的情况,如您的情况,有一个替代方案 std::index sequence
:
// helper for folding
template <typename T, std::size_t /*ignored*/>
using always_t = T;
template <typename T, typename Seq>
class VectorImpl;
template <typename T, std::size_t... Is>
class VectorImpl<T, std::index_sequence<Is...>>
{
public:
VectorImpl(always_t<T, Is>... args); // equivalent to VectorImpl(T arg1, T arg2, ..)
// ...
};
template <typename T, std::size_t N>
using Vector = VectorImpl<T, std::make_index_sequence<N>>;
我刚刚了解了 C++ 中的可变参数模板。我实现了,但我想知道,它能做到以下几点吗?
如果我想要一个参数数量可变的函数,我可以这样做:
template <typename... Ts>
f(Ts... args);
但是我失去了类型安全(我不知道参数的类型)。
如果我知道我的函数只需要 float
作为参数怎么办?我想在编译时确保每个参数都是我想要的类型。
所以这些是我的问题:
有没有办法强制使用可变参数模板(像这样)的特定类型?
template <float... Fs> f(Fs... args); // unlimited number of arguments but only float
如果不是,有没有办法在编译时检查它?
static_assert(std::is_same<A,B>)
在大多数情况下都很好,但它不适用于模板化的 类 (比如我的用例):template <typename T, uint16_t dimension> class Vector { template <typename... Ts> Vector(Ts... args) { static_assert(sizeof...(args) == dimension); static_assert(std::is_same_v<Ts..., T>()); //doesn't work because Ts will //develop into a lot of template //arguments. Just putting Ts doesn't //work either. } }
Ps:是的,我可以使用 std::vector
或 std::array
作为参数,但这不是重点。另外,我想保留漂亮的 Vector(2.0, 1.0, 0.0)
语法,而不是使用大括号。
如果编译器支持C++ 20那么你可以这样写
#include <type_traits>
template <typename T, uint16_t dimension>
class Vector
{
public:
template <typename... Ts>
Vector( Ts &&... args ) requires ( sizeof...( args ) == dimension ) && std::conjunction_v<std::is_same<T, std::decay_t<Ts>>...>
{
}
};
//...
Vector<float, 5> v( 1.1f, 2.2f, 3.3f, 4.4f, 5.5f );
或者如@HolyBlackCat在评论中所写,您也可以写
template <typename T, uint16_t dimension>
class Vector
{
public:
template <typename... Ts>
Vector( std::same_as<T> auto ...args ) requires( sizeof...( args ) == dimension )
{
}
};
template <typename T, uint16_t dimension>
class MyVector
{
public:
template <typename... Ts>
MyVector(Ts... args)
{
static_assert(sizeof...(args) == dimension);
static_assert((std::is_same<T,Ts>::value && ...) );
}
};
也可以使用C++17提供的一元右折叠表达式 https://en.cppreference.com/w/cpp/language/fold
这基本上是
std::is_same<T,Ts_1>::value && ( std::is_same<T,Ts_2>::value && ... std::is_same<T,Ts_n>::value ) ...)
并且在参数包 Ts 为空时也有效。引自参考 link:
" Logical AND (&&). The value for the empty pack is true"
对于固定矢量的情况,如您的情况,有一个替代方案 std::index sequence
:
// helper for folding
template <typename T, std::size_t /*ignored*/>
using always_t = T;
template <typename T, typename Seq>
class VectorImpl;
template <typename T, std::size_t... Is>
class VectorImpl<T, std::index_sequence<Is...>>
{
public:
VectorImpl(always_t<T, Is>... args); // equivalent to VectorImpl(T arg1, T arg2, ..)
// ...
};
template <typename T, std::size_t N>
using Vector = VectorImpl<T, std::make_index_sequence<N>>;