以下代码在注释掉概念时打印所有内容:
#include <cstdint>
#include <concepts>
#include <print>
#include <type_traits>
template<typename T>
struct ValueHolder {
using ValueType = T;
T value;
};
template<
typename Value,
typename T = std::remove_reference_t<Value>::ValueType,
// typename Arg,
typename Callable
>
// requires
// std::invocable<Callable, T>
// std::assignable_from<Arg, T>
// std::convertible_to<T, Arg>
// && std::invocable<Callable, Arg>
void callOnValue(Value&& val, Callable&& callable)
{
std::forward<Callable>(callable)(std::forward<Value>(val).value);
}
void printI32(std::int32_t) { std::println("i32"); }
void printI32Ref(std::int32_t&) { std::println("i32&"); }
void printI32RefRef(std::int32_t&&) { std::println("i32&&"); }
void printConstI32Ref(const std::int32_t &) { std::println("const i32&"); }
void printI16(std::int16_t) { std::println("i16"); }
void printI64(std::int64_t) { std::println("i64"); }
int main()
{
ValueHolder i{static_cast<std::int32_t>(42)};
callOnValue(i, printI32);
callOnValue(i, printI32Ref);
callOnValue(ValueHolder{static_cast<std::int32_t>(42)}, printI32RefRef);
const auto ci{i};
callOnValue(ci, printI32);
callOnValue(ci, printConstI32Ref);
callOnValue(i, printI16);
callOnValue(i, printI64);
}
取消评论
requires
std::invocable<Callable, T>
无法拨打电话
printIntRef
。取消注释 Arg
以及 std::assignable_from
或 std::convertible_to
会导致无法推断出 Arg
。
callable
上的什么概念将允许重现所有默认传递(即callable
接受按值传递、通过 const ref、通过左值的 ref 和通过右值的 ref-ref 传递)?printI16
不能编译,printI64
可以)怎么办?T
中的ValueHolder
是Derived&
或Derived*
,如何约束callable
为逆变,即接受Base&
或Base*
?Derived
到Base
)?问题在于你对自己真正想做的事情感到困惑。
首先,
callOnValue
应该是这样的
void callOnValue(Value&& val, Callable&& callable)
{
std::forward<Callable>(callable)(std::forward_like<Value>(val.value));
}
否则,您会在正确转发会员值时遇到问题。
std::forward_like
来自 C++23,因此您可以使用替代实现:
template<class T, class U>
constexpr auto&& forward_like(U&& x) noexcept
{
constexpr bool is_adding_const = std::is_const_v<std::remove_reference_t<T>>;
if constexpr (std::is_lvalue_reference_v<T&&>)
{
if constexpr (is_adding_const)
return std::as_const(x);
else
return static_cast<U&>(x);
}
else
{
if constexpr (is_adding_const)
return std::move(std::as_const(x));
else
return std::move(x);
}
}
第二个问题是模板中的
Value
可能是常量,所以要引用ValueType
的Value
最好这样写。
typename T = std::remove_cvref_t<Value>::ValueType
第三个问题是如何测试是否可以正确调用。怎么做呢?您需要将
Value
的特征复制到 T
并测试结果的可调用性。您可以通过自定义类型特征来做到这一点:
template<class T, class U>
struct copy_traits
{
using type = decltype(forward_like<T>(std::declval<U>()));
};
template<class T, class U>
using copy_traits_t = typename copy_traits<T,U>::type;
其中
forward_like
如上所述,并测试其可调用性 by std::invocable<Callable, copy_traits_t<Value, T> >
你会得到这样的代码:
#include <cstdint>
#include <concepts>
#include <iostream>
#include <type_traits>
#include <utility>
template<typename T>
struct ValueHolder {
using ValueType = T;
T value;
};
template<class T, class U>
constexpr auto&& forward_like(U&& x) noexcept
{
constexpr bool is_adding_const = std::is_const_v<std::remove_reference_t<T>>;
if constexpr (std::is_lvalue_reference_v<T&&>)
{
if constexpr (is_adding_const)
return std::as_const(x);
else
return static_cast<U&>(x);
}
else
{
if constexpr (is_adding_const)
return std::move(std::as_const(x));
else
return std::move(x);
}
}
template<class T, class U>
struct copy_traits
{
using type = decltype(forward_like<T>(std::declval<U>()));
};
template<class T, class U>
using copy_traits_t = typename copy_traits<T,U>::type;
template<
typename Value,
typename Callable,
typename T = std::remove_cvref_t<Value>::ValueType
>
requires (std::invocable<Callable, copy_traits_t<Value, T> >)
void callOnValue(Value&& val, Callable&& callable)
{
std::forward<Callable>(callable)(forward_like<Value>(val.value));
}
void printI32(std::int32_t) { std::cout << "i32\n"; }
void printI32Ref(std::int32_t&) { std::cout << "i32&\n"; }
void printI32RefRef(std::int32_t&&) {std::cout << "i32&&\n"; }
void printConstI32Ref(const std::int32_t &) {std::cout << "const i32&\n"; }
void printI16(std::int16_t) { std::cout << "i16\n"; }
void printI64(std::int64_t) { std::cout << "i16\n"; }
int main()
{
ValueHolder i{static_cast<std::int32_t>(42)};
callOnValue(i, printI32);
callOnValue(i, printI32Ref);
callOnValue(ValueHolder{static_cast<std::int32_t>(42)}, printI32RefRef);
const auto ci{i};
callOnValue(ci, printI32);
callOnValue(ci, printConstI32Ref);
callOnValue(i, printI16);
callOnValue(i, printI64);
}
或者,您也可以编写一个自定义概念:
requires (requires (Value&& val, Callable&& callable)
{
{std::forward<Callable>(callable)(forward_like<Value>(val.value))};
})