gpt4 book ai didi

c++ - 类型推导失败,指针指向成员方法

转载 作者:太空狗 更新时间:2023-10-29 19:39:28 24 4
gpt4 key购买 nike

我有以下充当代理的模板类。它有一个名为 call 的方法它应该用于调用包装对象上的方法。它有问题。类型推导失败,我不明白为什么。

Hudsucker::f需要 std::string然后不管我是否通过 std::stringconst引用它,编译器能够调用正确的方法。

但在 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,因为它有对比信息。从成员函数的类型,它会推导出 Astd::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 of A 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 of A 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(来自您的函数模板),Astd::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 to T 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_ifstd::is_convertible 不是 C++03 标准库的一部分。幸运的是,Boost 有自己的 enable_ifis_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/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com