C++:静态断言仿函数的参数是常量引用
C++: static assert that functor's argument is const reference
我正在 C++17
中编写一个模板函数,它接受一个仿函数 F
作为参数,我想限制传入的仿函数只有一个常量引用参数,其中 T
可以是任何类型。
例如:
template <class T> struct my_struct{
std::vector<T> collection;
template <class F> std::vector<T> func(F f){
static_assert(
// CONDITION HERE!,
"f's argument is not const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
显然,如果 f
是 [](auto v){return true;}
,则从 func
返回的结果向量将包含空元素(因为这些元素在添加到结果容器之前已被移动)。所以,我需要将可能的输入仿函数限制为 [](const auto& v){}
.
我试过这样的事情:
static_assert(
std::is_invocable_v<F, const T&>,
"f's argument is not const reference"
);
但是 func([](auto v){})
不会触发断言,因为 T
在我的例子中是可复制的。
func([](auto& v){})
也通过了测试,因为 auto
可以是 const T
.
但我需要将可能的 lambda 限制为 func([](const auto& v){})
。
您可能会编写特征(有其局限性),例如:
template <typename Sig> struct callable_traits;
template <typename Ret, typename ...Args>
struct callable_traits<Ret(*)(Args...)>
{
using args = std::tuple<Args...>;
};
// add specialization for C-ellipsis too
template <typename Ret, class C, typename ...Args>
struct callable_traits<Ret(C::*)(Args...) const>
{
using args = std::tuple<Args...>;
};
// add specialization for variant with C-ellipsis, cv-qualifier, ref-qualifier
template <class C>
struct callable_traits<C> : callable_traits<&C::operator()>{};
特征的限制:不处理模板化 operator()
(对于通用 lambda),重载 operator()
.
然后
template <class T> struct my_struct{
template <class F> void func(F f){
static_assert(
std::is_same_v<std::tuple<const T&>, typename callable_traits<F>::args>,
"f's argument is not const reference"
);
// here goes some code which can possibly call f with rvalue
// reference argument, so I want to avoid situation when the
// argument object is moved or modified. I don't have control
// over this code because it an STL algorithm.
}
};
我可能误解了你想做什么,但正如我所读,你想接受一个可调用对象,然后将一些参数传递给它并保证参数不能改变(你不希望有人将参数接受为非常量左值引用或右值引用。如果是这样,那么 std::is_invocable 就足够了:
#include <type_traits> // for std::is_invocable
#include <functional> // for std::invoke
template <class parameter_t> struct my_struct {
template <typename callable_t>
void function(callable_t const &callable) requires (
std::is_invocable<callable_t, parameter_t const &>::value
) {
// . . .
std::invoke(callable, parameter_t{});
// . . .
}
};
然后:
int main() {
my_struct<int>{}.function([](int const&){}); // Fine
my_struct<int>{}.function([](int){}); // Fine, a copy of the parameter is made when the lambda is invoked.
my_struct<int>{}.function([](int &){}); // error: no matching member function for call to 'function'
my_struct<int>{}.function([](int &&){}); // error: no matching member function for call to 'function'
}
(你可以玩一下here)
一个可能的问题是此方法确实允许制作副本,但如果主要目标是保护您持有的变量免受更改,这应该足够好。
P. S. 我知道我使用 c++20 requires
子句作为未来验证答案的一种方式,但是将它转换为 if constexpr
、static_assert
或任何其他方式应该是微不足道的你喜欢
我终于通过以下方式实现了:
template <class T> struct my_struct{
std::vector<T> collection;
struct noncopyable_value_type : T{
noncopyable_value_type(const noncopyable_value_type&) = delete;
noncopyable_value_type& operator=(const noncopyable_value_type&) = delete;
};
template <class F> std::vector<T> func(F f){
static_assert(
std::is_invocable_v<F, noncopyable_value_type>,
"f's argument must be const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
但是,这里的问题仍然是它只适用于通用 lambda。
我正在 C++17
中编写一个模板函数,它接受一个仿函数 F
作为参数,我想限制传入的仿函数只有一个常量引用参数,其中 T
可以是任何类型。
例如:
template <class T> struct my_struct{
std::vector<T> collection;
template <class F> std::vector<T> func(F f){
static_assert(
// CONDITION HERE!,
"f's argument is not const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
显然,如果 f
是 [](auto v){return true;}
,则从 func
返回的结果向量将包含空元素(因为这些元素在添加到结果容器之前已被移动)。所以,我需要将可能的输入仿函数限制为 [](const auto& v){}
.
我试过这样的事情:
static_assert(
std::is_invocable_v<F, const T&>,
"f's argument is not const reference"
);
但是 func([](auto v){})
不会触发断言,因为 T
在我的例子中是可复制的。
func([](auto& v){})
也通过了测试,因为 auto
可以是 const T
.
但我需要将可能的 lambda 限制为 func([](const auto& v){})
。
您可能会编写特征(有其局限性),例如:
template <typename Sig> struct callable_traits;
template <typename Ret, typename ...Args>
struct callable_traits<Ret(*)(Args...)>
{
using args = std::tuple<Args...>;
};
// add specialization for C-ellipsis too
template <typename Ret, class C, typename ...Args>
struct callable_traits<Ret(C::*)(Args...) const>
{
using args = std::tuple<Args...>;
};
// add specialization for variant with C-ellipsis, cv-qualifier, ref-qualifier
template <class C>
struct callable_traits<C> : callable_traits<&C::operator()>{};
特征的限制:不处理模板化 operator()
(对于通用 lambda),重载 operator()
.
然后
template <class T> struct my_struct{
template <class F> void func(F f){
static_assert(
std::is_same_v<std::tuple<const T&>, typename callable_traits<F>::args>,
"f's argument is not const reference"
);
// here goes some code which can possibly call f with rvalue
// reference argument, so I want to avoid situation when the
// argument object is moved or modified. I don't have control
// over this code because it an STL algorithm.
}
};
我可能误解了你想做什么,但正如我所读,你想接受一个可调用对象,然后将一些参数传递给它并保证参数不能改变(你不希望有人将参数接受为非常量左值引用或右值引用。如果是这样,那么 std::is_invocable 就足够了:
#include <type_traits> // for std::is_invocable
#include <functional> // for std::invoke
template <class parameter_t> struct my_struct {
template <typename callable_t>
void function(callable_t const &callable) requires (
std::is_invocable<callable_t, parameter_t const &>::value
) {
// . . .
std::invoke(callable, parameter_t{});
// . . .
}
};
然后:
int main() {
my_struct<int>{}.function([](int const&){}); // Fine
my_struct<int>{}.function([](int){}); // Fine, a copy of the parameter is made when the lambda is invoked.
my_struct<int>{}.function([](int &){}); // error: no matching member function for call to 'function'
my_struct<int>{}.function([](int &&){}); // error: no matching member function for call to 'function'
}
(你可以玩一下here)
一个可能的问题是此方法确实允许制作副本,但如果主要目标是保护您持有的变量免受更改,这应该足够好。
P. S. 我知道我使用 c++20 requires
子句作为未来验证答案的一种方式,但是将它转换为 if constexpr
、static_assert
或任何其他方式应该是微不足道的你喜欢
我终于通过以下方式实现了:
template <class T> struct my_struct{
std::vector<T> collection;
struct noncopyable_value_type : T{
noncopyable_value_type(const noncopyable_value_type&) = delete;
noncopyable_value_type& operator=(const noncopyable_value_type&) = delete;
};
template <class F> std::vector<T> func(F f){
static_assert(
std::is_invocable_v<F, noncopyable_value_type>,
"f's argument must be const reference"
);
std::vector<T> ret;
std::copy_if(
std::make_move_iterator(this->collection.begin()),
std::make_move_iterator(this->collection.end()),
std::inserter(ret, ret.end()),
f
);
return ret;
}
};
但是,这里的问题仍然是它只适用于通用 lambda。