模板参数作为函数未命名参数
Template argument as function unnamed argument
这个问题继续
这是我用来 return std::string
数据类型表示的未命名参数函数
struct Boo {};
struct Foo {};
std::string class2str(const double) { return "Floating"; };
std::string class2str(const int) { return "Fixed Point"; };
std::string class2str(const Foo) { return "Class Foo"; };
std::string class2str(const Boo) { return "Class Boo"; };
int main(int argc, char* argv[])
{
int x_a;
double x_b;
Foo F;
Boo B;
std::cout << "x_a :" << class2str(x_a) << std::endl;
std::cout << "x_b :" << class2str(x_b) << std::endl;
std::cout << "Foo :" << class2str(F) << std::endl;
std::cout << "Boo :" << class2str(B) << std::endl;
};
对于非静态成员的类型推导,我使用模板:
struct Foo { double A = 33; }
template<typename Class, typename MemType>
std::string class2str(MemType Class::* mData)
{
return class2str(MemType{}); // Use of empty constructor
}
std::cout << "Foo::A :" << class2str(&Foo::A) << std::endl;
但是这个模板需要创建一个带有空构造函数的对象,而这个对象可能根本不存在
struct Boo
{
double A;
Boo() = delete;
Boo(int x) :A(x) {};
};
struct Foo
{
double A = 33;
Boo b{ 0 };
};
// Compilation error: use of deleted function ‘Boo::Boo()’
std::cout << "Boo::b :" << class2str(&Foo::b) << std::endl;
如何在不调用空构造函数的情况下实现这个功能?
制作一个 class 模板并将其专门用于各种类型:
template <typename T> struct TypeNameHelper {};
template <> struct TypeNameHelper<int> { static constexpr std::string_view name = "Integer"; };
template <> struct TypeNameHelper<float> { static constexpr std::string_view name = "Real"; };
我还会为更短的语法添加一个变量模板,并在需要时预处理类型:
template <typename T>
inline constexpr std::string_view TypeName = TypeNameHelper<std::remove_cvref_t<T>>::name;
那么你可以这样做:
std::cout << TypeName<int> << '\n';
您当前所有的重载都采用对象。您可能会采用 type 或 object which hold type:
template <typename T> struct Tag{};
std::string class2str(Tag<double>){ return "Floating";};
std::string class2str(Tag<int>){ return "Fixed Point";};
std::string class2str(Tag<Foo>){ return "Class Foo";};
std::string class2str(Tag<Boo>){ return "Class Boo";};
template<typename Class, typename MemType>
std::string class2str(Tag<MemType Class::*>)
{
return class2str(Tag<MemType> {});
}
使用情况:
int main(int argc, char *argv[]) {
int x_a;
double x_b;
Foo F;
Boo B;
std::cout<< "x_a :" << class2str(Tag<decltype(x_a)>{}) <<std::endl;
std::cout<< "x_b :" << class2str(Tag<decltype(x_b)>{}) <<std::endl;
std::cout<< "Foo :" << class2str(Tag<decltype(F)>{}) <<std::endl;
std::cout<< "Boo :" << class2str(Tag<decltype(B)>{}) <<std::endl;
// or
std::cout<< "int :" << class2str(Tag<int>{}) <<std::endl;
};
(当我开始写答案时,问题没有答案,但当我正要 post 它时,我看到了@Jarod42 的答案,其中已经显示了标签分发方法。尽管如此,还是发布了这个答案,因为它使用了一种稍微不同的方法,即对已删除的主模板进行完全专业化,而不是非模板重载)
您可以使用标签调度来委托调用:
#include <iostream>
struct Boo {
double A;
Boo() = delete;
Boo(int x) : A(x){};
};
struct Foo {
double A = 33;
Boo b{0};
};
namespace detail {
template <typename T> struct Tag {};
template <typename T> std::string class2str_impl(Tag<T>) = delete;
template <> std::string class2str_impl(Tag<double>) { return "Floating"; };
template <> std::string class2str_impl(Tag<int>) { return "Fixed Point"; };
template <> std::string class2str_impl(Tag<Foo>) { return "Class Foo"; };
template <> std::string class2str_impl(Tag<Boo>) { return "Class Boo"; };
} // namespace detail
template <typename T> std::string class2str(T) {
return class2str_impl(detail::Tag<T>{});
}
template <typename Class, typename MemType>
std::string class2str(MemType Class::*) {
return class2str_impl(detail::Tag<MemType>{});
}
int main() {
int x_a{42};
double x_b{4.2};
Foo F{};
Boo B{x_a};
std::cout << "x_a :" << class2str(x_a) << std::endl;
std::cout << "x_b :" << class2str(x_b) << std::endl;
std::cout << "Foo :" << class2str(F) << std::endl;
std::cout << "Boo :" << class2str(B) << std::endl;
std::cout << "Boo::b :" << class2str(&Foo::b) << std::endl;
};
其中 class2str_impl
的主要模板可以删除(如上所述),或者实现给定类型没有映射字符串的自定义消息。
标准库中常用的技巧是使用declval.
它是为这个确切的用例而设计的。
或者更简单
template<typename Class, typename MemType>
std::string class2str(MemType Class::*){
return class2str(*reinterpret_cast<MemType*>(0)); // Never use the value, it's null
}
一个想法可能是使用函数参数来调用只需要模板参数的函数重载:
struct Boo {
double A;
Boo()= delete;
Boo(int x) :A(x){};
};
struct Foo {
double A = 33;
Boo b{0};
};
#include <type_traits>
#include <utility>
template<class T> std::string class2str(); // primary
// specializations
template<> std::string class2str<double>(){ return "Floating"; };
template<> std::string class2str<int>(){ return "Fixed Point"; };
template<> std::string class2str<Foo>(){ return "Class Foo"; };
template<> std::string class2str<Boo>(){ return "Class Boo"; };
// taking by argument
template<class T>
std::string class2str(const T&) {
return class2str<std::remove_cv_t<std::remove_reference_t<T>>>();
}
// class member by argument
template<typename Class, typename MemType>
std::string class2str(MemType Class::*) {
return class2str<std::remove_cv_t<std::remove_reference_t<MemType>>>();
}
int main() {
int x_a;
double x_b;
Foo F;
Boo B{1};
std::cout<< "x_a :" << class2str(x_a) <<std::endl;
std::cout<< "x_b :" << class2str(x_b) <<std::endl;
std::cout<< "Foo :" << class2str(F) <<std::endl;
std::cout<< "Boo :" << class2str(B) <<std::endl;
std::cout<< "Foo::A :" << class2str(&Foo::A) <<std::endl;
std::cout<< "Foo::b :" << class2str(&Foo::b) <<std::endl;
std::cout<< "Boo::A :" << class2str(&Boo::A) <<std::endl;
};
输出:
x_a :Fixed Point
x_b :Floating
Foo :Class Foo
Boo :Class Boo
Foo::A :Floating
Foo::b :Class Boo
Boo::A :Floating
与其他答案中讨论的标签分发和专业化技术不同,这里是使用 c++17's constexpr if
.
的不同方法
首先,我们使用特征(mem_type
)
从成员指针中找到成员的类型
其次,我们编写一个内部辅助函数(即helper::class2str()
),它使用编译时类型检查并丢弃错误分支(即if constexpr
),这样我们正确地 return 将数据类型表示为 const char*
文字(因为我们可以使函数 constexpr
)!
最后,我们将有 main class2str()
,它实际检查模板参数类型是否为成员指针,并使用 if constexpr
再次执行分支。如果模板类型是成员指针,我们使用特征 mem_type
获取成员类型并将其传递给 helper::class2str()
.
#include <type_traits> // std::is_same_v, std::is_member_pointer_v
// trait to get the member type
template<typename Class> struct mem_type {};
template<typename MemType, typename Class> struct mem_type<MemType Class::*> {
using type = MemType;
};
// alias for mem_type<T>
template<typename Type> using mem_type_t = typename mem_type<Type>::type;
namespace helper
{
template<typename Type> constexpr auto class2str() noexcept
{
if constexpr (std::is_same_v<Type, int>) return "Fixed Point";
else if constexpr (std::is_same_v<Type, double>) return "Floating";
else if constexpr (std::is_same_v<Type, Boo>) return "Class Boo";
else if constexpr (std::is_same_v<Type, Foo>) return "Class Foo";
}
}
template<typename Type>
std::string class2str()
{
if constexpr (std::is_member_pointer_v<Type>)
return helper::class2str<mem_type_t<Type>>();
else
return helper::class2str<Type>();
}
现在您可以像这样使用它:
std::cout << "x_a :" << class2str<int>() << '\n';
std::cout << "x_b :" << class2str<double>() << '\n';
std::cout << "Boo::b :" << class2str<decltype(&Boo::A)>() << '\n';
std::cout << "Foo::b :" << class2str<decltype(&Foo::b)>() << '\n';
这里是(the complete demo)
这个问题继续
这是我用来 return std::string
数据类型表示的未命名参数函数
struct Boo {};
struct Foo {};
std::string class2str(const double) { return "Floating"; };
std::string class2str(const int) { return "Fixed Point"; };
std::string class2str(const Foo) { return "Class Foo"; };
std::string class2str(const Boo) { return "Class Boo"; };
int main(int argc, char* argv[])
{
int x_a;
double x_b;
Foo F;
Boo B;
std::cout << "x_a :" << class2str(x_a) << std::endl;
std::cout << "x_b :" << class2str(x_b) << std::endl;
std::cout << "Foo :" << class2str(F) << std::endl;
std::cout << "Boo :" << class2str(B) << std::endl;
};
对于非静态成员的类型推导,我使用模板:
struct Foo { double A = 33; }
template<typename Class, typename MemType>
std::string class2str(MemType Class::* mData)
{
return class2str(MemType{}); // Use of empty constructor
}
std::cout << "Foo::A :" << class2str(&Foo::A) << std::endl;
但是这个模板需要创建一个带有空构造函数的对象,而这个对象可能根本不存在
struct Boo
{
double A;
Boo() = delete;
Boo(int x) :A(x) {};
};
struct Foo
{
double A = 33;
Boo b{ 0 };
};
// Compilation error: use of deleted function ‘Boo::Boo()’
std::cout << "Boo::b :" << class2str(&Foo::b) << std::endl;
如何在不调用空构造函数的情况下实现这个功能?
制作一个 class 模板并将其专门用于各种类型:
template <typename T> struct TypeNameHelper {};
template <> struct TypeNameHelper<int> { static constexpr std::string_view name = "Integer"; };
template <> struct TypeNameHelper<float> { static constexpr std::string_view name = "Real"; };
我还会为更短的语法添加一个变量模板,并在需要时预处理类型:
template <typename T>
inline constexpr std::string_view TypeName = TypeNameHelper<std::remove_cvref_t<T>>::name;
那么你可以这样做:
std::cout << TypeName<int> << '\n';
您当前所有的重载都采用对象。您可能会采用 type 或 object which hold type:
template <typename T> struct Tag{};
std::string class2str(Tag<double>){ return "Floating";};
std::string class2str(Tag<int>){ return "Fixed Point";};
std::string class2str(Tag<Foo>){ return "Class Foo";};
std::string class2str(Tag<Boo>){ return "Class Boo";};
template<typename Class, typename MemType>
std::string class2str(Tag<MemType Class::*>)
{
return class2str(Tag<MemType> {});
}
使用情况:
int main(int argc, char *argv[]) {
int x_a;
double x_b;
Foo F;
Boo B;
std::cout<< "x_a :" << class2str(Tag<decltype(x_a)>{}) <<std::endl;
std::cout<< "x_b :" << class2str(Tag<decltype(x_b)>{}) <<std::endl;
std::cout<< "Foo :" << class2str(Tag<decltype(F)>{}) <<std::endl;
std::cout<< "Boo :" << class2str(Tag<decltype(B)>{}) <<std::endl;
// or
std::cout<< "int :" << class2str(Tag<int>{}) <<std::endl;
};
(当我开始写答案时,问题没有答案,但当我正要 post 它时,我看到了@Jarod42 的答案,其中已经显示了标签分发方法。尽管如此,还是发布了这个答案,因为它使用了一种稍微不同的方法,即对已删除的主模板进行完全专业化,而不是非模板重载)
您可以使用标签调度来委托调用:
#include <iostream>
struct Boo {
double A;
Boo() = delete;
Boo(int x) : A(x){};
};
struct Foo {
double A = 33;
Boo b{0};
};
namespace detail {
template <typename T> struct Tag {};
template <typename T> std::string class2str_impl(Tag<T>) = delete;
template <> std::string class2str_impl(Tag<double>) { return "Floating"; };
template <> std::string class2str_impl(Tag<int>) { return "Fixed Point"; };
template <> std::string class2str_impl(Tag<Foo>) { return "Class Foo"; };
template <> std::string class2str_impl(Tag<Boo>) { return "Class Boo"; };
} // namespace detail
template <typename T> std::string class2str(T) {
return class2str_impl(detail::Tag<T>{});
}
template <typename Class, typename MemType>
std::string class2str(MemType Class::*) {
return class2str_impl(detail::Tag<MemType>{});
}
int main() {
int x_a{42};
double x_b{4.2};
Foo F{};
Boo B{x_a};
std::cout << "x_a :" << class2str(x_a) << std::endl;
std::cout << "x_b :" << class2str(x_b) << std::endl;
std::cout << "Foo :" << class2str(F) << std::endl;
std::cout << "Boo :" << class2str(B) << std::endl;
std::cout << "Boo::b :" << class2str(&Foo::b) << std::endl;
};
其中 class2str_impl
的主要模板可以删除(如上所述),或者实现给定类型没有映射字符串的自定义消息。
标准库中常用的技巧是使用declval.
它是为这个确切的用例而设计的。
或者更简单
template<typename Class, typename MemType>
std::string class2str(MemType Class::*){
return class2str(*reinterpret_cast<MemType*>(0)); // Never use the value, it's null
}
一个想法可能是使用函数参数来调用只需要模板参数的函数重载:
struct Boo {
double A;
Boo()= delete;
Boo(int x) :A(x){};
};
struct Foo {
double A = 33;
Boo b{0};
};
#include <type_traits>
#include <utility>
template<class T> std::string class2str(); // primary
// specializations
template<> std::string class2str<double>(){ return "Floating"; };
template<> std::string class2str<int>(){ return "Fixed Point"; };
template<> std::string class2str<Foo>(){ return "Class Foo"; };
template<> std::string class2str<Boo>(){ return "Class Boo"; };
// taking by argument
template<class T>
std::string class2str(const T&) {
return class2str<std::remove_cv_t<std::remove_reference_t<T>>>();
}
// class member by argument
template<typename Class, typename MemType>
std::string class2str(MemType Class::*) {
return class2str<std::remove_cv_t<std::remove_reference_t<MemType>>>();
}
int main() {
int x_a;
double x_b;
Foo F;
Boo B{1};
std::cout<< "x_a :" << class2str(x_a) <<std::endl;
std::cout<< "x_b :" << class2str(x_b) <<std::endl;
std::cout<< "Foo :" << class2str(F) <<std::endl;
std::cout<< "Boo :" << class2str(B) <<std::endl;
std::cout<< "Foo::A :" << class2str(&Foo::A) <<std::endl;
std::cout<< "Foo::b :" << class2str(&Foo::b) <<std::endl;
std::cout<< "Boo::A :" << class2str(&Boo::A) <<std::endl;
};
输出:
x_a :Fixed Point
x_b :Floating
Foo :Class Foo
Boo :Class Boo
Foo::A :Floating
Foo::b :Class Boo
Boo::A :Floating
与其他答案中讨论的标签分发和专业化技术不同,这里是使用 c++17's constexpr if
.
首先,我们使用特征(
从成员指针中找到成员的类型mem_type
)其次,我们编写一个内部辅助函数(即
helper::class2str()
),它使用编译时类型检查并丢弃错误分支(即if constexpr
),这样我们正确地 return 将数据类型表示为const char*
文字(因为我们可以使函数constexpr
)!最后,我们将有 main
class2str()
,它实际检查模板参数类型是否为成员指针,并使用if constexpr
再次执行分支。如果模板类型是成员指针,我们使用特征mem_type
获取成员类型并将其传递给helper::class2str()
.
#include <type_traits> // std::is_same_v, std::is_member_pointer_v
// trait to get the member type
template<typename Class> struct mem_type {};
template<typename MemType, typename Class> struct mem_type<MemType Class::*> {
using type = MemType;
};
// alias for mem_type<T>
template<typename Type> using mem_type_t = typename mem_type<Type>::type;
namespace helper
{
template<typename Type> constexpr auto class2str() noexcept
{
if constexpr (std::is_same_v<Type, int>) return "Fixed Point";
else if constexpr (std::is_same_v<Type, double>) return "Floating";
else if constexpr (std::is_same_v<Type, Boo>) return "Class Boo";
else if constexpr (std::is_same_v<Type, Foo>) return "Class Foo";
}
}
template<typename Type>
std::string class2str()
{
if constexpr (std::is_member_pointer_v<Type>)
return helper::class2str<mem_type_t<Type>>();
else
return helper::class2str<Type>();
}
现在您可以像这样使用它:
std::cout << "x_a :" << class2str<int>() << '\n';
std::cout << "x_b :" << class2str<double>() << '\n';
std::cout << "Boo::b :" << class2str<decltype(&Boo::A)>() << '\n';
std::cout << "Foo::b :" << class2str<decltype(&Foo::b)>() << '\n';
这里是(the complete demo)