C++函数模板从成员类型中推导类型?
C++ funtion template deduction of type from member type?
我正在尝试编写一个 C++ 函数模板,当使用 classes 成员别名的参数调用时,该函数可以推导出该别名的 class。 简化的 版本会更好:
class A {
public :
using x_t = float;
void whatami() {printf("A\n");}
};
class B {
public :
using x_t = int;
void whatami() {printf("B\n");}
};
template<typename T>
void fn(T::x_t x) {
T t;
t.whatami();
};
...
A::x_t a;
fn(a);
...
g++ 7.3 错误是:
play$ g++ --std=c++11 x.cpp
x.cpp:19:12: error: variable or field ‘fn’ declared void
void fn(T::x_t x) {
^~~
x.cpp:19:16: error: expected ‘)’ before ‘x’
void fn(T::x_t x) {
^
x.cpp: In function ‘int main(int, char**)’:
x.cpp:29:3: error: ‘fn’ was not declared in this scope
fn(a);
^~
我将其解释为 C++ 无法像 fn(T::x_t x)
那样推导出 T 但我不确定。
想法?
下面的函数应该使用对象(T),因为你想调用它的成员函数。
template<typename T>
void fn(T& x)
{
x.whatami();
};
模板函数的调用方式如下:
A a;
fn(a);
它将打印结果为 => A
这是你想要的,对吧?
它不能从float
推导出T
,这就是a
。在尝试填充您的模板时,它不会对 typedef 进行任何特殊考虑。它也不会在整个类型 space 中搜索具有该名称成员的任何类型。
获得该推导的一种方法是使用对所属 class 的引用来提升该成员类型,并添加一个隐式转换回原语:
template <typename TOwner, typename TData>
class x_t {
TData data;
public:
constexpr x_t() = default;
constexpr x_t(TData data) : data(data) { }
constexpr operator TData() const { return data; }
};
struct A {
using x_t = x_t<A, float>;
/*...*/
};
然后部分特化将正常工作:
template<typename TOwner, typename TData>
void fn(x_t<TOwner, TData> x) {
TOwner t;
t.whatami();
};
并且您可以在任何类型安全的函数中使用实例
A::x_t a(42.0);
std::cout << a; // 42.0
(遗憾的是,printf
不算作类型安全。在这里使用流,优秀的 fmt 库中的 fmt::printf
也可以)。
它可能无法达到您容易从成员推断的目标,但这是可能的。
从任意模板id推导就像解任意方程,这远远超出了编译器的能力。想想这个:
template <int N>
using S = std::integral_constant<int, /* a super complex radical involving 42nd powers and 84th roots */>;
template <int N>
constexpr int solve(S<N>)
{
return N;
}
你觉得编译器可以solve(std::integral_constant<int, 420>{});
?没有。
在您的情况下,模板 ID 仅限于有限数量的情况,因此帮助编译器的一种简单的非侵入式方法是您自己求解方程式:
template <typename x_t>
using solve = std::conditional_t<
std::is_same_v<x_t, float>, A,
std::conditional_t<
// similar code for B
>
>;
我正在尝试编写一个 C++ 函数模板,当使用 classes 成员别名的参数调用时,该函数可以推导出该别名的 class。 简化的 版本会更好:
class A {
public :
using x_t = float;
void whatami() {printf("A\n");}
};
class B {
public :
using x_t = int;
void whatami() {printf("B\n");}
};
template<typename T>
void fn(T::x_t x) {
T t;
t.whatami();
};
...
A::x_t a;
fn(a);
...
g++ 7.3 错误是:
play$ g++ --std=c++11 x.cpp
x.cpp:19:12: error: variable or field ‘fn’ declared void
void fn(T::x_t x) {
^~~
x.cpp:19:16: error: expected ‘)’ before ‘x’
void fn(T::x_t x) {
^
x.cpp: In function ‘int main(int, char**)’:
x.cpp:29:3: error: ‘fn’ was not declared in this scope
fn(a);
^~
我将其解释为 C++ 无法像 fn(T::x_t x)
那样推导出 T 但我不确定。
想法?
下面的函数应该使用对象(T),因为你想调用它的成员函数。
template<typename T>
void fn(T& x)
{
x.whatami();
};
模板函数的调用方式如下:
A a;
fn(a);
它将打印结果为 => A 这是你想要的,对吧?
它不能从float
推导出T
,这就是a
。在尝试填充您的模板时,它不会对 typedef 进行任何特殊考虑。它也不会在整个类型 space 中搜索具有该名称成员的任何类型。
获得该推导的一种方法是使用对所属 class 的引用来提升该成员类型,并添加一个隐式转换回原语:
template <typename TOwner, typename TData>
class x_t {
TData data;
public:
constexpr x_t() = default;
constexpr x_t(TData data) : data(data) { }
constexpr operator TData() const { return data; }
};
struct A {
using x_t = x_t<A, float>;
/*...*/
};
然后部分特化将正常工作:
template<typename TOwner, typename TData>
void fn(x_t<TOwner, TData> x) {
TOwner t;
t.whatami();
};
并且您可以在任何类型安全的函数中使用实例
A::x_t a(42.0);
std::cout << a; // 42.0
(遗憾的是,printf
不算作类型安全。在这里使用流,优秀的 fmt 库中的 fmt::printf
也可以)。
它可能无法达到您容易从成员推断的目标,但这是可能的。
从任意模板id推导就像解任意方程,这远远超出了编译器的能力。想想这个:
template <int N>
using S = std::integral_constant<int, /* a super complex radical involving 42nd powers and 84th roots */>;
template <int N>
constexpr int solve(S<N>)
{
return N;
}
你觉得编译器可以solve(std::integral_constant<int, 420>{});
?没有。
在您的情况下,模板 ID 仅限于有限数量的情况,因此帮助编译器的一种简单的非侵入式方法是您自己求解方程式:
template <typename x_t>
using solve = std::conditional_t<
std::is_same_v<x_t, float>, A,
std::conditional_t<
// similar code for B
>
>;