将const this传递给接受const指针的函数是否不符合const正确性?

3

我有一个类模板 Foo,其中包含以下成员函数:

bool contains(const T& item) const

我已经使用指针类型实例化了它:Foo<Bar*>,因此我期望成员函数现在将具有以下签名:

bool contains(const Bar*& item) const

在一个 constBar 成员函数中,我试图将 this 传递给 Foo<Bar*>::contains:
bool Bar::func(const Foo<Bar*>& foo) const
{
    return foo.contains(this);
}

以下代码无法编译,会出现以下错误:

错误:从“const Bar*”转换到“Bar*”时无效

问题:

  • 为什么我的const T&参数不符合常量正确性?
  • Foo<T>::contains(...) const的哪个签名需要允许使用this进行编译调用?

完整示例:

#include <vector>
#include <algorithm>

template<typename T>
struct Foo
{
    bool contains(const T& item) const
    {
        return false;
    }
};

struct Bar
{
    bool func(const Foo<Bar*>& foo) const
    {
        return foo.contains(this);
    }
};

错误输出:

scratch/main.cpp:17:33: error: invalid conversion from ‘const Bar*’ to ‘Bar*’ [-fpermissive]
         return foo.contains(this);
                                 ^
scratch/main.cpp:7:10: note: initializing argument 1 of ‘bool Foo<T>::contains(const T&) const [with T = Bar*]
     bool contains(const T& item) const
2个回答

6

I have instantiated this with a pointer type: Foo<Bar*>, leading me to expect that the member function will now have the following signature:

bool contains(const Bar*& item) const

这就是问题所在。当 T = Bar* 时,表达式

bool contains(const T& item) const

实际上将编译为:
bool contains(Bar * const & item) const

那就是一个指向Bar的常量指针的引用。如果你仔细想一下,这是有道理的:你希望T是const,然后你想要对它进行引用。
如果你想以通常的方式应用const(虽然这可能会对经验丰富的C++程序员造成一些意外),你可以按照以下方式声明容器和成员函数:
template <class T>
class Container {
public:
    using const_bare_type = typename std::conditional<
        std::is_pointer<T>::value,
        typename std::remove_pointer<T>::type const*,
        const T>::type;

    bool contains(const const_bare_type& item);
};

我能对我的Foo成员函数进行任何更改,以便允许我将this传递给它吗? - Steve Lorimer
@SteveLorimer 不是的,当实例化 Foo<Bar*> 时会出现错误,请参考我的回答。 - vladon
@YamMarcovic 看起来有点作弊呢 ;-) - vladon
@YamMarcovic 嗯,我希望我的 Foo 类可以适用于指针类型和非指针类型。因此,在签名中它应该只需要一个 const ref to T - 我需要有两个重载并在指针类型上使用 enable_if 的 SFINAE 来使其工作吗? - Steve Lorimer
1
@SteveLorimer 有点像。在类定义中,您不一定需要有2个重载。您可以使用一些特性来定义const_bare_type,它将为非指针类型产生const T,并为指针类型产生const T *。然后声明contains(const_bare_type&item) - Yam Marcovic
1
@SteveLorimer 我添加了一些关于最后一部分的示例代码,并删除了过于专业化的指针内容。 - Yam Marcovic

0
编译器提示错误的行,你必须写成:
bool func(const Foo<const Bar*>& foo) const

例如,在模板参数中使用const Bar*,因为Bar::funcconst Bar * this作为其参数接收,并且无法将其转换为模板参数中的Bar*(无法删除const)。


我不相信我能将 Foo<Bar*> 改为 Foo<const Bar*>,对吗? - Steve Lorimer
编译器将foo<T>和foo<const T>视为两种完全不同且无关的类型。 - Steve Lorimer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接