在VC2012中我是一个奇怪的人我似乎无法解决将const指针通过const引用传递给模板化类的函数的语法,模板化参数是非常量指针,即:
template<typename T>
struct Foo
{
void Add( const T& Bar ) { printf(Bar); }
};
void main()
{
Foo<char*> foo;
const char* name = "FooBar";
foo.Add(name); // Causes error
}
所以我在这里简化了我的问题,但基本上我希望'Add'的参数有一个const T即const char *。我试过了:
void Add( const (const T)& Bar );
typedef const T ConstT;
void Add( const (ConstT)& Bar );
void Add( const typename std::add_const<T>::type& Bar );
这些都不起作用。我得到的确切错误是:
error C2664: 'Foo<T>::Add' : cannot convert parameter 1 from 'const char *' to 'char *const &'
with
[
T=char *
]
Conversion loses qualifiers
我能看到的是正确的,但是如果不将常量'name'变为非const,我该如何解决它。
答案 0 :(得分:7)
指向常量对象(T const*
或const T*
)的指针与指向非常量对象(T * const
)的常量指针之间存在很大差异。在您的情况下,成员Add
的签名是:
void Foo<char *>::Add(char * const& ); // reference to a constant pointer to a
// non-constant char
我通常建议人们完全放弃在左侧使用const
,因为初学者通常会将typedef
(或推断类型)与类型替换混淆,当他们阅读时:
const T& [T == char*]
他们误解了
const char*&
如果const
位于右侧位置:
T const &
初学者的事情比较简单,因为简单的心理替代是有效的:
char * const &
与您提出的问题不同,但也许您认为自己想要的问题是:
如果T
类型U
不是指针类型,则const T
类型的函数需要T
X const *
。T
如果X
是指向template <typename T>
struct add_const_here_or_there {
typedef T const type;
};
template <typename T>
struct add_const_here_or_there<T*> {
typedef T const * type;
};
template <typename T>
void Foo<T>::Add( const typename add_const_here_or_there<T>::type & arg ) {
...
然后你可以在你的签名中使用它:
const
请注意,我在签名中添加了两个char*
,因此在您的情况下char const * const &
会映射到const&
,因为您似乎想要传递const
某事,你也希望指向的类型为{{1}}。
你可能想知道元函数的名称:* add_const_here_or_there *,它就是这样的原因:没有简单的方法来描述你想要做什么,这通常是代码味道。但是你有解决方案。
答案 1 :(得分:2)
看起来你的问题是,只要你有一个映射到模板类型的指针类型,你就不能再将指针添加到指向类型,只能指向指针本身。你试图做的是自动添加constness到你的函数的参数(所以如果T是char*
,函数应该接受const char* const&
而不是char* const&
,因为你已经书面)。唯一的方法是使用另一个模板将constness添加到指针类型的指针,如下所示。我冒昧地包含了丢失的标题并更正了main
的签名:
#include <cstdio>
template<typename T>
struct add_const_to_pointee
{
typedef T type;
};
template <typename T>
struct add_const_to_pointee<T*>
{
typedef const T* type;
};
template<typename T>
struct Foo
{
void Add( typename add_const_to_pointee<T>::type const & Bar ) { printf(Bar); }
};
int main()
{
Foo<char*> foo;
const char* name = "FooBar";
foo.Add(name); // Causes error
}
正如另一个人所提到的,如果您使用std::string
而不是C风格的字符串,这个问题就会消失。
答案 2 :(得分:1)
您需要将Foo对象的模板参数更改为Foo<const char*>
。因为如果T=char*
,则为const T=char*const
,而不是const char*
。试图强迫它发挥作用并不是一个好主意,可能会导致未定义的行为。
答案 3 :(得分:1)
使用:
Foo<const char*> foo;
const char* name = "FooBar";
foo.Add(name);
并写下int main()
而不是void main()
答案 4 :(得分:1)
如果无法将const char*
而不是char*
传递给Foo
,则可以使用std::remove_pointer
精确打印正确的类型。这将删除指针修饰符,并允许您提供更明确的类型。
#include <type_traits>
template<typename T>
struct Foo
{
void Add(typename std::remove_pointer<T>::type const*& Bar ) { printf(Bar); }
};
为了防止修改指针值,您也可以将引用声明为const
。
void Add(typename std::remove_pointer<T>::type const* const& Bar )
{ Bar = "name"; } // <- fails
如果您需要将类型从指针指向,请将std::decay
与std::remove_pointer
一起使用
void Add(typename std::remove_pointer<typename std::decay<T>::type>::type const*& Bar)
{
printf(Bar);
}
这实际上取决于您对T
的要求。我建议假设只将基类型(例如char
)作为T
传递,并从中构建引用和指针类型。