我有一个类,其构造函数需要一些向量并存储它们。
struct X {
X(std::vector<int> const& ints, std::vector<float> const& floats, std::vector<std::string> const& strings)
: ints_{ints}
, floats_{floats}
, strings_{strings}
{};
std::vector<int> ints_;
std::vector<float> floats_;
std::vector<std::string> strings_;
};
我想将成员变量转换为引用,因为在生产代码中,传递给构造函数的值是左值,其生命周期比类X的对象长。
但是,单元测试通常用临时构造X
es,如下所示:
X x{ {42}, {3.14f}, {"hello"} };
如果要引用X
的成员,则应阻止此类呼叫。这可以通过编写一个构造函数来完成,该构造函数接受rvalue引用并使其成为=delete
。
X(std::vector<int> && ints, std::vector<float> && floats, std::vector<std::string> && strings) = delete;
如果所有参数都是临时参数,这将阻止实例化。不幸的是,它允许通过调用,其中至少有一个参数是左值:
std::vector<std::string> no_strings;
X x{ {42}, {3.14f}, no_strings };
因为左值引用愿意绑定到rvalues,所以可以调用(const&,const&,const&)
构造函数。
我是否必须编写lvalue / rvalue ref参数的所有组合(全部七个)并将所有这些参数标记为已删除?
答案 0 :(得分:3)
template<class T, class U>
using is_lvalue_reference_to_possibly_const = std::integral_constant<bool,
std::is_same<T, const U&>::value || std::is_same<T, U&>::value>;
template<class VI, class VF, class VS>
using check_value_cat_for_ctor = typename std::enable_if<
is_lvalue_reference_to_possibly_const<VI, std::vector<int>> &&
is_lvalue_reference_to_possibly_const<VF, std::vector<float>> &&
is_lvalue_reference_to_possibly_const<VS, std::vector<string>>>::type;
template<class VI, class VF, class VS, class = check_value_cat_for_ctor<VI, VF, VS>>
X(VI&&, VF&&, VS&&) {
// ...
}
答案 1 :(得分:3)
具有非const lvalue ref参数的函数怎么样?临时工不能与那些人绑定,所以这应该是你想要的。
像this:
#include <string>
#include <vector>
struct X {
X(std::vector<int>& ints,
std::vector<float>& floats,
std::vector<std::string>& strings)
: ints_{ints}
, floats_{floats}
, strings_{strings}
{};
std::vector<int>& ints_;
std::vector<float>& floats_;
std::vector<std::string>& strings_;
};
int main()
{
X x{ {42}, {3.14f}, {"hello"} };
}
你唯一丢失的是const
正确性。它可以像构造函数一样薄。也就是说,如果你想要const
。你的问题根本没有说清楚。
因为@ T.C.答案中的模板让我的眼睛流血,如果你更倾向于这个解决方案,这里是my version of those:
#include <string>
#include <type_traits>
#include <vector>
template<typename ... Ts>
constexpr bool are_not_temporaries = (std::is_lvalue_reference<Ts>::value&&...);
struct X
{
template<typename VI, typename VF, typename VS,
typename = std::enable_if_t<are_not_temporaries<VI, VF, VS>, void>>
X(VI&& ints, VF&& floats, VS&& strings)
: ints_{ints}
, floats_{floats}
, strings_{strings}
{};
std::vector<int>& ints_;
std::vector<float>& floats_;
std::vector<std::string>& strings_;
};
int main()
{
X x{ {42}, {3.14f}, {"hello"} };
}
这使用C ++ 17折叠表达式来扩展可变参数模板参数的参数包。如果C ++ 17不可用,您可以将其替换为:
template<typename T1, typename T2, typename T3>
constexpr bool are_not_temporaries = std::is_lvalue_reference<T1>::value
&& std::is_lvalue_reference<T2>::value
&& std::is_lvalue_reference<T3>::value;
诚然,这不太好。
答案 2 :(得分:2)
如果你只是顺从图书馆怎么办?
template <typename T>
using vector_ref = std::reference_wrapper<std::vector<T> const>;
struct X {
X(vector_ref<int> ints, vector_ref<float> floats, vector_ref<std::string> strings);
};
std::reference_wrapper
已经只能从左值开始构建,因此我们不需要自己完成所有工作。