如何将 decltype(T::member) 用于 T 中可能不存在的成员

How to use decltype(T::member) for a member that might not exist in T

提问人:DottyPhone 提问时间:9/23/2023 最后编辑:Jan SchultkeDottyPhone 更新时间:9/23/2023 访问量:118

问:

我正在寻找一种方法来定义一个具有类型的变量,具体取决于我的类模板化所针对的类型的成员的类型,但需要注意的是,成员变量可能不存在。由于我只在成员确实存在的情况下访问块中的变量,因此我不关心声明它或它是什么类型。但是,我了解到这是行不通的,因为未使用的分支仍然需要编译。constexprtypename conditional<hasMember<T>, decltype(T::member), int>

以下是我希望开始工作的一个例子:

#include <iostream>
using namespace std;

struct X{};
struct Y{string member;};

template<class T>
concept hasMember = requires (T t) {t.member;};

template<class T>
struct A{
    void hi(T a) {
            // stuff
           typename conditional<hasMember<T>, decltype(T::member), int /*member is unused if hasMember<T> is false, so I don't care what type it is or if it exists*/>::type member;
           
           if constexpr (hasMember<T>){
               member = a.member;
           }
            // stuff
           if constexpr (hasMember<T>){
               std::cout << member<< std::endl;
           }
    };
};

int main() {
    X x;
    Y y{"hi"};
    
    // Does not compile 
    // A<X> ax;
    // ax.hi(x);
    
    // Compiles and runs fine
    A<Y> ay;
    ay.hi(y);

    return 0;
}
C++ 20 类型特征 C++-概念 decltype

评论

0赞 Jan Schultke 9/23/2023
请注意,您尝试执行的操作与以下操作非常相似:stackoverflow.com/q/70257565/5740428
0赞 Jarod42 9/23/2023
什么是,或者换句话说,为什么要拆分块?//stuffif constexpr

答:

3赞 Ted Lyngmo 9/23/2023 #1

您可以创建一个老式类型特征来获取该类型:

template<class T, class O>
struct member_or {
    static O test(...);

    template<class U = T>
    static auto test(int) -> decltype(U::member);

    using type = decltype(test(0));
};

template<class T, class O>
using member_or_t = member_or<T, O>::type;

然后,您的实现可以是:A

template <class T>
struct A {
    void hi(T a) {
        // stuff
        member_or_t<T, int> member;    // <- now no problem

        if constexpr (hasMember<T>) {
            member = a.member;
        }
        // stuff
        if constexpr (hasMember<T>) {
            std::cout << member << std::endl;
        }
    };
};

评论

0赞 DottyPhone 9/23/2023
谢谢,我已经去了,这很好用。最后一个问题,是否可以创建一个以使其更通用,或者它必须是一个语句?member_or_t<T, std::monostate> member;variable_or_t<T, member, std::monostate>#define
1赞 Ted Lyngmo 9/23/2023
@DottyPhone 不客气。我认为你需要涉及宏才能做出这样的东西。
5赞 Jan Schultke 9/23/2023 #2

@TedLyngmo的解决方案是可靠的,还有更多选择。 您可以简单地依靠编译器已经延迟实例化所有模板的事实,并定义一个类模板:

template <typename T>
struct member_type {
    using type = decltype(T::member);
};

只要您不访问 ,就不会发生实例化。 这可以在以下方面被利用:::typemember_typestd::conditional_t

using M = std::conditional_t<hasMember<T>, member_type<T>, std::type_identity<int>>::type;
M member;

选择包含这些类型的类模板,而不是有条件地选择 和 。然后,仅访问所选类模板,其中用作回退。decltype(T::member)int::type::typestd::type_identity

1赞 康桓瑋 9/23/2023 #3

您可以只使用 lambda 初始化局部变量,例如memebr

void hi(T a) {
       auto member = [&] {
         if constexpr (hasMember<T>)
           return a.member;
         else
           return 0;
       }();

        // stuff
       if constexpr (hasMember<T>){
           std::cout << member<< std::endl;
       }
};
0赞 Jarod42 9/23/2023 #4

由于我只在成员确实存在的情况下访问块中的变量,因此我不关心声明它或它是什么类型。constexpr

那么为什么要在非 constexpr 块中使用它呢?

你可以简单地重写你的函数,这样:

template<class T>
struct A{
    void hi(T a) {
        // ...
        auto do_stuff = [](){ /*stuff*/ };
        if constexpr (hasMember<T>){
            using MemberType = decltype(T::member);
            const MemberType member = a.member;
            do_stuff();
            std::cout << member << std::endl;
        } else {
            do_stuff();
        }
        // ...
    };
};

演示