我有一个函数,它接受std::function
作为参数。但是,我想确保传入的函数不允许修改传递给它的参数。
这是函数的简单版本(注意T
可以是,通常是参考):
template <class T>
void Bar(std::function<void(std::add_const_t<T>)> func)
{
// ...
}
不良用法:
Bar<int&>([](int&) { /* Do nasty stuff! */ }); /* A-OK! */
我想禁止这种用法,但是这段代码编译完全正常,即使我觉得它不应该。
有趣的是,如果我摆脱了模板参数,即:
void Bar(std::function<void(const int&)> func)
{
// ...
}
然后,这种用法不会编译(因为它不应该):
Bar([](int&) { /* Do nasty stuff! */ }); /* Error C2664 */
如何强制执行此操作并仍然保留模板参数?
答案 0 :(得分:5)
请注意,std::add_const_t<int &>
为int &
,因为您未将const
添加到int
。相反,您要将const
添加到对int
的引用中,并获得对int
(即int &
)的const引用,而不是对const int
的引用
解决它的一个简单方法可以是:
#include<functional>
#include<type_traits>
template<typename T>
struct to_const_ { using type = std::add_const_t<T>; };
template<typename T>
struct to_const_<T &> { using type = std::add_const_t<T> &; };
template<typename T>
using to_const_t = typename to_const_<T>::type;
template <class T>
void Bar(std::function<void(to_const_t<T>)> func)
{}
int main() {
Bar<int&>([](int&) {});
}
上述代码无法编译(按要求),除非您将其转换为:
Bar<int&>([](const int &) {});
请注意,它只适用于左值引用,但如果您有了这个想法,则添加对右值引用和指针的支持很简单。
它遵循一个最小的(可能)工作示例:
#include<functional>
#include<type_traits>
template<typename T>
struct to_const_ { using type = std::add_const_t<T>; };
template<typename T>
struct to_const_<T &> { using type = std::add_const_t<T> &; };
template<typename T>
struct to_const_<T &&> { using type = std::add_const_t<T> &&; };
template<typename T>
struct to_const_<T * const> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type * const, std::add_const_t<typename to_const_<T>::type> * const>; };
template<typename T>
struct to_const_<T *> { using type = std::conditional_t<std::is_pointer<T>::value, typename to_const_<T>::type *, std::add_const_t<typename to_const_<T>::type> *>; };
template<typename T>
using to_const_t = typename to_const_<T>::type;
template <class T>
void Bar(std::function<void(to_const_t<T>)> func)
{}
int main() {
Bar<int **>([](const int **) {});
}
答案 1 :(得分:0)
在模板的帮助下,您可以使用static_assert
进行编译失败。
//Base case - T is non-const, possibly pointer or reference
template <typename T> struct is_const_param { static constexpr bool value = !std::is_lvalue_reference<T>::value && !std::is_rvalue_reference<T>::value && !std::is_pointer<T>::value; };
//T is const, but possibly pointer to non-const
template <typename T> struct is_const_param<const T> { static constexpr bool value = !std::is_pointer<T>::value; };
//Remove reference, try again
template <typename T> struct is_const_param<const T&> : is_const_param<const T> { };
//Remove reference, try again
template <typename T> struct is_const_param<const T&&> : is_const_param<const T> { };
//Remove pointer, try again
template <typename T> struct is_const_param<const T*> : is_const_param<const T> { };
//Remove pointer, try again
template <typename T> struct is_const_param<const T* const> : is_const_param<const T> { };
static_assert
看起来像是:
template <class T>
void Bar(std::function<void(T)>)
{
static_assert(is_const_param<T>::value, "...");
}
此设置仅在T
为: