可选成员的 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)
深入研究它。只有您可以决定这种方法能走多远。
我正在尝试从 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)
深入研究它。只有您可以决定这种方法能走多远。