英文:
Extracting the underlying type in the template
问题
I am new to C++20. The intention here is to have a template class which has value
whose type would be the underlying type of T
that's passed in.
So in case of T
being:
std::optional<char>
, it'schar value
int
, it's justint value
.
-
Is there any better way to extract the types than through
struct TypeExtract
? More or a generic solution in C++20 perhaps? Given if the class could take more than juststd::optional<int>
or just a primitive type? -
Can the condition in
foo
be improved specially with the wayval
is initialized?
英文:
I am new to C++20. The intention here is to have a template class which has value
whose type would be the underlying type of T
that's passed in.
So in case of T
being:
std::optional<char>
, it'schar value
int
, it's justint value
.
-
Is there any better way to extract the types than through
struct TypeExtract
? More or a generic solution in C++20 perhaps? Given if the class could take more than juststd::optional<int>
or just a primitive type? -
Can the condition in
foo
be improved specially with the wayval
is initialized?
template<typename T>
constexpr bool is_optional = false;
template<typename T>
constexpr bool is_optional<std::optional<T>> = true;
template<typename T>
struct TypeExtract
{
using type = T;
};
template<typename T>
struct TypeExtract<std::optional<T>>
{
using type = T;
};
template <typename T>
concept is_integral = std::is_integral_v<typename TypeExtract<T>::type>;
template <is_integral T>
class A
{
using Type = typename TypeExtract<T>::type;
Type val;
void foo(T value)
{
if constexpr (is_optional<T>)
{
val = *value;
}
else
{
val = value;
}
}
};
int main()
{
A<char> a1;
A<std::optional<int>> a2;
// A<double> a3; // fails
}
答案1
得分: 1
代码部分不翻译,以下是翻译好的内容:
It looks like you're trying to extract first template parameter from a class template and keep on unwinding templates until you get to a non-template type. In that case you could make a type trait that is specialized for types instantiated from templates:
你似乎想要从类模板中提取第一个模板参数,并继续展开模板,直到达到非模板类型。在这种情况下,你可以创建一个专门用于从模板实例化的类型的类型特征:
You could then use it like so:
然后你可以像这样使用它:
英文:
It looks like you're trying to extract first template parameter from a class template and keep on unwinding templates until you get to a non-template type. In that case you could make a type trait that is specialized for types instantiated from templates:
// primary template
template<class T, class...>
struct type {
using value_type = T;
};
// specialization for template instantiated types
template<template<class, class...> class T, class F, class... Rest>
struct type<T<F, Rest...>> {
using value_type = typename type<F>::value_type;
};
// helper alias
template<class... Ts>
using type_t = typename type<Ts...>::value_type;
You could then use it like so:
int main() {
type_t<char> a1;
type_t<std::optional<int>> a2;
type_t<double, int> a3;
static_assert(std::is_same_v<decltype(a1), char>);
static_assert(std::is_same_v<decltype(a2), int>);
static_assert(std::is_same_v<decltype(a3), double>);
}
答案2
得分: 0
以下是翻译的内容:
这里没有好坏之分,这是一种风格和约定的问题,但个人认为可以去掉if constexpr
,利用尾随要求来减少函数的圈复杂度。另一方面,这会增加一些样板代码。选择权在你手里。
关于类型提取,虽然不太重要,但我可能会使用一个模板基类,并将其成员导入类中,而不是将类型导入类中。这对我来说更符合惯例。
至于概念,我可能会在类型特征的位置使用更多库提供的概念。
附注:在从可选类型赋值时,考虑使用assert
、.value()
或类似的函数,以确保它不为空。
总的来说,我可能会以以下方式编写你的代码:
#include <concepts>
#include <type_traits>
#include <optional>
template<typename T>
concept StdOptional = std::same_as<std::optional<typename T::value_type>, T>;
template<typename T>
concept OptionalIntegral = StdOptional<T> and std::integral<typename T::value_type>;
template<typename T>
concept OptionalOrOptionalIntegral = std::integral<T> or OptionalIntegral<T>;
template<typename>
struct ABase;
template<std::integral T>
struct ABase<T>
{
T value;
};
template<OptionalIntegral T>
struct ABase<T>
{
typename T::value_type value;
};
template<OptionalOrOptionalIntegral T>
class A : ABase<T>
{
using ABase<T>::value;
public:
void setValue(T val) requires(std::integral<T>)
{
value = val;
}
void setValue(T val) requires(OptionalIntegral<T>)
{
value = val.value();
}
};
演示:https://godbolt.org/z/dzvr9xbGr
英文:
There is no good or bad here, it's a matter of style and convention, but personally I would get rid of if constexpr
and take advantage of trailing requires for the sake of reducing function's cyclomatic complexity. On the other hand, that add some boilerplate. The choice is yours.
Not much can be done about type extraction, though I would probably use a templated base and import its member(s) instead of importing the type into the class. Not a big deal, but it feels more idiomatic to me.
As for concepts, I'd probably use more library-provided ones in the place of type traits.
Side note: consider using assert
, .value()
or similar function when assigning from the optional to ensure it's not empty.
All in all, I'd probably write your code somewhat this way:
#include <concepts>
#include <type_traits>
#include <optional>
template<typename T>
concept StdOptional = std::same_as<std::optional<typename T::value_type>, T>;
template<typename T>
concept OptionalIntegral = StdOptional<T> and std::integral<typename T::value_type>;
template<typename T>
concept OptionalOrOptionalIntegral = std::integral<T> or OptionalIntegral<T>;
template<typename>
struct ABase;
template<std::integral T>
struct ABase<T>
{
T value;
};
template<OptionalIntegral T>
struct ABase<T>
{
typename T::value_type value;
};
template<OptionalOrOptionalIntegral T>
class A : ABase<T>
{
using ABase<T>::value;
public:
void setValue(T val) requires(std::integral<T>)
{
value = val;
}
void setValue(T val) requires(OptionalIntegral<T>)
{
value = val.value();
}
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论