多个模板相关的参数



我不确定问题的名称是否真的反映了我在这里问的问题,所以请告诉我是否有更好的标题。

我有一个表示值的类,它可以通过可调用对象初始化,也可以更简洁地通过对变量的引用初始化。

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

请注意,现在它失败了,因为我对构造函数有冲突的概念,不允许两个构造函数同时初始化。然而,如果没有这些概念,它们根本无法编译。有办法解决这个问题吗?

另一种解决方案是让该类包含另一个类的元组,其中该类可以通过单个变量或可调用对象构造。

不幸的是,这需要像下面这样的语法,对每个参数进行显式强制转换。

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参数,另一个仅接受-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&&...);

简化:

template<class... T>
requires ((std::invocable<T> or std::is_lvalue_reference_v<T>) and...)
Values(T&&...);

处理构造函数的声明,现在仍然是定义

对于构造函数的初始化,你可以使用:

template<class... T>
requires ((std::invocable<T> or 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 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;
}())>...>;

相关内容

  • 没有找到相关文章

最新更新