可选成员的 decltype

Decltype of optional member

我正在尝试从 std::optional<> 中的结构成员获取类型,即成员函数的 return 类型。

这是一个简化的例子:

struct Result
{
    int tag;
    int pos;
};

class Dict
{
public:
    std::optional<Result> search(const char *word)
    {
        return Result{ 1,2 };
    }
};

我希望能够做这样的事情:

int main()
{
    Dict abc;
    decltype(abc.search(const char*)->pos) position;

    return 0;
}

如果您将实际参数传递给 search,它将起作用(以及使 search public):

https://wandbox.org/permlink/0Q3mLW7SmQW4QshE

#include <optional>

struct Result
{
    int tag;
    int pos;
};

class Dict
{
public:
    std::optional<Result> search(const char *word)
    {
        return Result{ 1,2 };
    }
};

int main()
{
    Dict abc;
    decltype(abc.search("")->pos) position;

    return 0;
}

search 的参数不必有效(根据您的函数所期望的 - 因为它实际上不会调用它),它只需要是正确的类型即可。

如果你想直接处理类型,而不是实例,正如你的评论所建议的那样,@Jarod42 指出你可以使用以下行来声明变量:

decltype(std::declval<Dict>().search(std::declval<const char*>())->pos) position;

https://wandbox.org/permlink/kZlqKUFoIWv1m3M3

虽然我可能不需要指出 ~70 个字符的变量类型是多么不可读。我想如果是我,我要么只使用 int,要么为 pos 创建一个类型别名,例如using ResultPositionType = int; 然后在 Result 结构中使用它,然后在 main.

中再次使用它

马克直接回答了你的问题。

但是,我提出了一个更易于阅读的解决方案,它借鉴了标准库对类型别名的严重依赖。它们基本上是免费的,如果在 class 定义中有点嘈杂:

#include <optional>

struct Result
{
    using TagType = int;
    using PosType = int;

    TagType tag;
    PosType pos;
};

class Dict
{
public:
    using ResultType = Result;

    std::optional<ResultType> search(const char* word)
    {
        return ResultType{ 1, 2 };
    }
};

int main()
{
    Dict abc;
    Dict::ResultType::PosType position;
}

这具有自我记录的好处,并且不需要只有 C++ 标准语母语人士才能理解的 70 个字符的语句。 :)

它在 class 定义本身中也有价值,因为现在您可以在一个地方设置这些类型,并且在语义允许的情况下,可以比过去更容易地改变它们(当您有修改每一个成员函数,每一个数据成员……)。

一个缺点是两个 Result 成员的完整性不是很明显。这是否是一个问题取决于您的实际代码,特别是 Result 的实际大小及其常用方式。作为妥协,您可以考虑在该级别删除类型别名,仅保留 Dict::ResultType,然后使用 decltype(declval<Dict::ResultType>().pos) 深入研究它。只有您可以决定这种方法能走多远。