- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有以下充当代理的模板类。它有一个名为 call
的方法它应该用于调用包装对象上的方法。它有问题。类型推导失败,我不明白为什么。
Hudsucker::f
需要 std::string
然后不管我是否通过 std::string
或 const
引用它,编译器能够调用正确的方法。
但在 Hudsucker::g
的情况下with需要const
引用std::string
在 GCC 和 Clang 的两种情况下,类型推导都失败了。
第一行的 GCC 错误:
main.cpp:36:28: error: no matching function for call to ‘Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&)’
main.cpp:36:28: note: candidate is:
main.cpp:10:10: note: template<class A> void Proxy::call(void (T::*)(A), A) [with A = A; T = Hudsucker]
main.cpp:10:10: note: template argument deduction/substitution failed:
main.cpp:36:28: note: deduced conflicting types for parameter ‘A’ (‘const std::basic_string<char>&’ and ‘std::basic_string<char>’)
特别是这个有点奇怪:no matching function for call to Proxy<Hudsucker>::call(void (Hudsucker::*)(const string&), const string&)
.这正是我希望看到的签名。
第一行的 Clang 错误:
main.cpp:36:7: error: no matching member function for call to 'call'
p.call(&Hudsucker::g, s); // <- Compile error
~~^~~~
main.cpp:10:10: note: candidate template ignored: deduced conflicting types for parameter 'A' ('const std::basic_string<char> &' vs. 'std::basic_string<char>')
void call(void (T::*f)(A), A a)
代码:
#include <string>
#include <iostream>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A>
void call(void (T::*f)(A), A a)
{
(o_.*f)(a);
}
private:
T &o_;
};
class Hudsucker
{
public:
void f(std::string s) {}
void g(std::string const &s) {}
};
int main()
{
Hudsucker h;
Proxy<Hudsucker> p(h);
std::string const s = "For kids, you know.";
std::string const &r = s;
p.call(&Hudsucker::f, s);
p.call(&Hudsucker::f, r);
p.call(&Hudsucker::g, s); // <- Compile error
p.call(&Hudsucker::g, r); // <- Compile error
return 0;
}
您能解释一下为什么类型推导会以这种方式失败吗?有没有办法让它用 const
编译?引用资料?
最佳答案
编译器无法推断类型 A
,因为它有对比信息。从成员函数的类型,它会推导出 A
是 std::string const&
,而从第二个参数的类型,它会推导出它是 std::string
.
将您的函数模板更改为允许成员函数的参数和实际提供的参数具有不同类型的模板,然后 SFINAE-constrain 后者可转换为前者:
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
如果您想知道为什么要调用此函数:
std::string const s = "For kids, you know.";
// ...
p.call(&Hudsucker::g, s);
编译器会推断出 std::string
,这是因为 C++11 标准的第 14.8.2.1/2 段:
If
P
is not a reference type:— If
A
is an array type, the pointer type produced by the array-to-pointer standard conversion (4.2) is used in place ofA
for type deduction; otherwise,— If
A
is a function type, the pointer type produced by the function-to-pointer standard conversion (4.3) is used in place ofA
for type deduction; otherwise,— If A is a cv-qualified type, the top level cv-qualifiers of
A
’s type are ignored for type deduction.
在引用的段落中,P
是您的A
(来自您的函数模板),A
是std::string const
。这意味着 std::string const
中的 const
被类型推导忽略。为了更好地理解这一点,请考虑这个更简单的示例:
#include <type_traits>
template<typename T>
void foo(T t)
{
// Does NOT fire!
static_assert(std::is_same<T, int>::value, "!");
}
int main()
{
int const x = 42;
foo(x);
}
考虑第二个函数调用:
std::string const &r = s;
// ...
p.call(&Hudsucker::g, r);
原因是id-expression r
的类型是std::string const
。由于第 5/5 段,引用被删除:
If an expression initially has the type “reference to
T
” (8.3.2, 8.5.3), the type is adjusted toT
prior to any further analysis. The expression designates the object or function denoted by the reference, and the expression is an lvalue or an xvalue, depending on the expression.
现在我们回到了与第一次函数调用相同的情况。
正如 Mike Vine 在评论中指出的那样,在函数调用期间将第二个参数作为第一个(成员函数)参数的输入时,您可能希望完美转发第二个参数:
#include <utility> // For std::forward<>()
template <typename A, typename B,
typename std::enable_if<std::is_convertible<B, A>::value>::type* = nullptr>
void call(void (T::*f)(A), B&& a)
{
(o_.*f)(std::forward<B>(a));
}
如果您负担不起 C++11,那么将不允许您对模板参数使用默认参数。在这种情况下,您可以在返回类型上使用 SFINAE 约束:
template <typename A, typename B>
typename enable_if<is_convertible<B, A>::value>::type
// ^^^^^^^^^ ^^^^^^^^^^^^^^
// But how about these traits?
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
请注意,std::enable_if
和 std::is_convertible
不是 C++03 标准库的一部分。幸运的是,Boost 有自己的 enable_if
和 is_convertible
版本,所以:
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_convertible.hpp>
template <typename T> class Proxy
{
public:
Proxy(T &o): o_(o) {}
template <typename A, typename B>
typename boost::enable_if<boost::is_convertible<B, A>>::type
call(void (T::*f)(A), B a)
{
(o_.*f)(a);
}
private:
T &o_;
};
注意,boost::enable_if
接受一个 type 作为它的第一个模板参数,它定义了一个 value
bool 成员,而 std::enable_if
接受 bool 值。 Boost 中 std::enable_if
的等价物是 boost::enable_if_c
。
关于c++ - 类型推导失败,指针指向成员方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16794633/
有人能给我解释一下我下面程序中的一点吗我编写的以下程序是为了理解 int * const * var 的含义; #include "iostream" using namespace std ; in
我正在摆弄 C 中的指针,但仍然不确定一些非常基础的知识。我想出了以下示例代码: #include int main(void) { int num = 42; /
以下代码产生警告: const char * mystr = "\r\nHello"; void send_str(char * str); void main(void){ send_str
我正在尝试在我的 Linux 计算机上升级 cmake。这是我使用的命令。 删除旧的 cmake: sudo apt purge --auto-remove cmake 从 https://cmake
我试图借助 char 指针来追踪 union 第一个字节的值。我假设 byte1 的输出应该是 ff,但实际上是 ffffffff。如果我错了请纠正我。 #include #include #in
我正在使用微 Controller 进行一些 ADC 测量。当我尝试使用 -O2 优化编译以下代码时遇到问题,当代码中存在 PrintVal() 函数时,MCU 卡住。我做了一些调试,结果发现,当我添
#include #include using namespace std; int main () { vector qwerty; qwerty.push_back(5);
我有我的 woking setup.py 文件;其中包含以下行: home = os.environ["HOME"] # home = /home/dr in my machine; distu
这段代码 #include #include static_assert(std::is_same_v::value_type, volatile int>); 在最新的 GCC 和 clang
我对 C 中的前向声明有疑问。 代码 typedef struct yhash_s t_yhash;// forward declaration struct yhash_s { size_t
我想提取成员指针指向的成员的类型。 template void demo(myClass& instance, void* ptr) { instance.*member = *reinter
我正在尝试使用指针将一段 C 代码转换为 Swift 3。这是 C 代码中的相关部分。 Float32 sampleArray[256] = { // Array is 256 Float value
您可能知道,VLA's haves pros and cons 和它们在 C11 中是可选的。 我想使 VLA 成为可选项的主要原因是:“堆栈可能会爆炸”: int arr[n]; /* where
这段代码有什么错误?为什么我不能按照我尝试的方式取消引用该元素。 #include typedef struct { int value; struct node * left;
有什么方法可以在 JavaScript 中创建\返回指向变量的指针吗? 比如,在 PHP 中: function func() { ..... return &$result; } 我
如果您想使用方法的指针作为参数,则需要将该方法键入作为对象的函数,就像这样好 : type TAcceptor = function(filename:string):boolean of objec
很简单的问题: 我对 C++ 中的智能指针有点陌生。我想我得到了所有权的东西,但我不知道如何访问他们实际指向的内容。当我尝试使用对象的成员函数/变量时,我只是得到了 unique_ptr 类的函数,这
我得到了一个点的方位 Angular 、指南针方向和一个可以将箭头设置到某个方向的 api(0 是顶部,90 是右侧,180 是底部,360 是顶部) 如果我希望箭头指向我采用方位 Angular 形
我正在尝试找到一种方法,从单元格中获取位于当前工作表左侧(托盘下方)的工作表单元格中的数据。 我知道如何通过调用其他工作表 =Sheet1!A1 但现在我需要一些最好的解释 =Sheet[-1]!A1
所以我在 MATLAB 中有一个 for 循环,其中向量 x 将通过一个函数,比如 cos(x).^2,或者不同的选择,比如 sin(x).^2 + 9.*x。用户将在 for 循环之前选择他想使用的
我是一名优秀的程序员,十分优秀!