C++ C++;:静态断言函子';s参数是常量引用

C++ C++;:静态断言函子';s参数是常量引用,c++,c++17,metaprogramming,template-meta-programming,C++,C++17,Metaprogramming,Template Meta Programming,我正在C++17中编写一个模板函数,它接受一个函子F作为参数,我想限制传入的函子只有一个常量引用参数,其中T可以是任何类型 例如: 模板结构我的结构{ 病媒采集; 模板std::vector func(F){ 静态断言( //这里的条件!, “f的参数不是常量引用” ); std::载体ret; 复制( std::make_move_迭代器(this->collection.begin()), std::make_move_迭代器(this->collection.end()), 插入器(ret

我正在
C++17
中编写一个模板函数,它接受一个函子
F
作为参数,我想限制传入的函子只有一个常量引用参数,其中
T
可以是任何类型

例如:

模板结构我的结构{
病媒采集;
模板std::vector func(F){
静态断言(
//这里的条件!,
“f的参数不是常量引用”
);
std::载体ret;
复制(
std::make_move_迭代器(this->collection.begin()),
std::make_move_迭代器(this->collection.end()),
插入器(ret,ret.end()),
F
);
返回ret;
}
};
显然,如果
f
[](auto v){return true;}
func
返回的结果向量将包含空元素(因为这些元素在添加到结果容器之前被移动)。因此,我需要将可能的输入函子限制为
[](const auto&v){}

我试过这样的方法:

static\u断言(
std::是否可开票,
“f的参数不是常量引用”
);
但是
func([](auto v){})
不会触发断言,因为在我的例子中
T
是可复制的。
func([](auto&v){})
也通过了测试,因为
auto
可以是
const T

但是我需要将可能的lambda限制为
func([](const auto&v){})
您可以编写traits(及其限制),比如:

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()>{};

我可能误解了您的意图,但正如我所读到的,您希望接受一个callable,然后向它传递一些参数,并保证该参数不能更改(您不希望有人接受该参数作为非常量l值引用或r值引用。如果是这样,那么就足够了:

\include//for std::是否可开票
#include//for std::invoke
模板结构我的结构{
模板
void函数(可调用常量&可调用)需要(
std::is_invocable::value
) {
// . . .
调用(可调用,参数{});
// . . .
}
};
然后:

intmain(){
my_struct{}.function([](int const&){});//很好
my_struct{}.function([](int){});//很好,调用lambda时会复制参数。
my_struct{}.function([](int&){});//错误:调用“function”时没有匹配的成员函数
my_struct{}.function([](int&&){});//错误:调用“function”时没有匹配的成员函数
}
(你可以玩玩它)

一个可能的问题是,此方法确实允许创建副本,但是如果主要目标是保护您持有的变量不受更改的影响,那么这就足够了


另外,我知道我使用了c++20
requires
子句作为未来验证答案的方法,但是如果constexpr、
static\u assert
或您喜欢的任何其他方式,将其转换为
应该很简单。

我最终通过以下方式实现了它:


模板结构我的结构{
病媒采集;
结构不可复制\u值\u类型:T{
不可复制值类型(常量不可复制值类型&)=删除;
不可复制值类型&运算符=(常量不可复制值类型&)=delete;
};
模板std::vector func(F){
静态断言(
std::是否可开票,
“f的参数必须是常量引用”
);
std::载体ret;
复制(
std::make_move_迭代器(this->collection.begin()),
std::make_move_迭代器(this->collection.end()),
插入器(ret,ret.end()),
F
);
返回ret;
}
};

但是,这里的问题仍然是它只适用于通用lambda。

auto
本身可以是
const T&
”-它不能。它是一个值类型。它通过了测试,因为该类型是可复制的。您试图实现什么?为什么“仅按const ref”似乎是解决方案?@StoryTeller UnslanderMonica对,这是因为它是可复制的。正如我所说,我试图阻止移动或修改
F
的参数。听起来你只需要对传递给函数的东西进行右值到左值的转换。使用
const auto&&lvalue\u temp=expression\u生成_临时;
然后将
levalue\u temp
传递到
f
将确保它不能被移动或修改。那么为什么不简单地通过
std::as_const
传递它呢?它将触发修改(不考虑
const\u cast
,但我们应该假设一个正常的函子)。如果不是可扩展的,您也可以制作本地副本。让函子拥有这些副本。我为问题添加了更多信息,使用了或多或少精确的代码。可以看到,我正在使用移动迭代器和
std::copy\u if
。这可能会提供有关我需要的更多信息。不幸的是,这对我不起作用。请参阅原始更新的描述tion。感谢您的建议,这看起来应该有效。但是,不幸的是,有一个拦截器。您提到这不适用于泛型lambdas,但我希望能够实际使用泛型lambdas,这是至关重要的,因为编写精确的类型可能太难,这些类型可能很长。
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.
    }
};