我不确定问题名称是否真的反映了我在这里问的问题,所以如果有更好的标题,请告诉我。
我有一个代表一个值的类,它可以通过可调用对象初始化,或者更干净地通过对变量的引用来初始化。
template <typename F> concept not_callable = !std::invocable<F>;
template <typename... Args>
struct Values{
std::tuple<std::function<Args()>...> values;
Values(std::invocable auto&&... func): values{std::make_tuple(std::function(func)...)} {}
Values(not_callable auto&... args): values{std::make_tuple(std::function([&args](){return args;})...)} {}
};
Values(std::invocable auto&&... func) -> Values<std::remove_cvref_t<decltype(func())>...>;
Values(not_callable auto&... arg) -> Values<std::remove_cvref_t<decltype(arg)>...>;
这使我可以通过一系列函数或引用来构造函数。但是,我希望能够混合使用这些来初始化它。
int x;
char* y;
Values v1 (rand, clock);
Values v2 (x, y);
// Values v3 (x, rand); Doesn't work
请注意,现在它失败了,因为我对构造函数的概念存在冲突,不允许将两者一起初始化。但是,如果没有这些概念,它们根本无法编译。有没有办法让这项工作?
另一种解决方案是让此类包含另一个类的元组,其中该类可从单个 var 或可调用对象构造。
不幸的是,这需要像下面这样的语法,并对每个参数进行显式转换。
class singleV;
Value v4(singleV(x), singleV(rand));
有没有办法像
v3
一样初始化它?
可以定义辅助函数,根据参数的类型构造对应的
std::function
对象
template <typename T>
concept callable_or_variable =
std::invocable<T> || std::is_lvalue_reference_v<T>;
template <callable_or_variable T>
auto to_function(T&& t) {
if constexpr (std::invocable<T>)
return std::function<std::remove_cvref_t<std::invoke_result_t<T>>()>(t);
else
return std::function<std::remove_cvref_t<T>()>([&t] { return t; });
}
然后根据这个辅助函数返回的
std::function
类型的返回类型实现CTAD
template <typename... Args>
struct Values{
std::tuple<std::function<Args()>...> values;
Values(callable_or_variable auto&&... args):
values{to_function(std::forward<decltype(args)>(args))...} {}
};
template <callable_or_variable... Args>
Values(Args&&...) ->
Values<std::invoke_result_t<decltype(to_function(std::declval<Args>()))>...>;
目前,你有两个构造函数,一个只接受
std::invocable
参数,另一个只接受non-std::invocable
左值引用参数。首先,我们可以用不简洁的语法写出来:
template<class... T>
requires (std::invocable<T> and...)
Values(T&&... func);
template<class... T>
requires ((not std::invocable<T> and std::is_lvalue_reference_v<T>) and...)
Values(T&&... args);
然后我们可以将它们组合成一个构造函数:
template<class... T>
requires (std::invocable<T> and...) or ((not std::invocable<T> and std::is_lvalue_reference_v<T>) and...)
Values(T&&...);
然后,翻转逻辑,这样我们就有了析取的合取,而不是连取的析取:
template<class... T>
requires ((std::invocable<T> or (not std::invocable<T> and std::is_lvalue_reference_v<T>) and...)
Values(T&&...);
这处理了构造函数的声明,现在剩下的就是define了。
对于构造函数初始化器,您可以使用例如生活:
template<class... T>
requires ((std::invocable<T> or (not std::invocable<T> and std::is_lvalue_reference_v<T>) and...)
Values(T&&... args) : values{[&] {
if constexpr (std::invocable<T>)
return args;
else
return [&args] { return args; }
}()...} {}
对于 ctad 指南也是如此:
template<class... T>
requires ((std::invocable<T> or (not std::invocable<T> and std::is_lvalue_reference_v<T>) and...)
Values(T&&... args) -> Values<std::remove_cvref_t<decltype([]{
if constexpr (std::invocable<T>)
return args();
else
return args;
}())>...>;