代码片段不能在 C++17 下编译,命名空间问题?
Code snippet doesnot compile under C++17, namespace issue?
#include <iostream>
#include <vector>
namespace Cactus
{
namespace Json
{
class Value
{
public:
Value(){}
};
}
typedef std::vector< int > vectorInt;
inline Cactus::Json::Value& operator << (Cactus::Json::Value& os, const vectorInt& v)
{
return os;
}
inline Cactus::Json::Value& operator >> (Cactus::Json::Value& is, vectorInt& v)
{
return is;
}
}
namespace App
//namespace Cactus
{
void f()
{
Cactus::vectorInt ints;
Cactus::Json::Value val;
val << ints;
}
}
int main()
{
App::f();
}
遇到错误,
main.cpp: In function 'void App::f()':
main.cpp:38:13: error: no match for 'operator<<' (operand types are 'Cactus::Json::Value' and 'Cactus::vectorInt' {aka 'std::vector<int>'})
38 | val << ints;
| ~~~ ^~ ~~~~
| | |
| | Cactus::vectorInt {aka std::vector<int>}
| Cactus::Json::Value
但是如果使用“命名空间仙人掌”,它会编译。但是我必须使用“namespace App”,有什么建议吗?
更新:问题已解决。
将两个运算符移动到全局命名空间有效。
或者添加使用 Cactus 也可以。
那些运算符重载必须在您的 f
函数所在的范围内。当函数 f
在 Cactus
命名空间中时,这些运算符重载在范围内。但是当它在命名空间 App
下时,那些就超出了范围。因此,要么将这些运算符重载放在全局命名空间中,要么将运算符重载放在与 class Value
相同的命名空间中,或者将使用该运算符的所有函数放在相同的命名空间中。另一种方法是在函数内部使用 using
语句,使用它们将它们导入到该函数的全局范围。
它被称为ADL
。至少,在您的示例中,函数 Cactus::Json::Value& operator << (Cactus::Json::Value& os, const vectorInt& v)
无法在参数类型 val
的关联 classes 或名称空间中找到。因为 ADL
应受以下规则约束:
For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes are determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. [...]
If an associated namespace is an inline namespace, its enclosing namespace is also included in the set. If an associated namespace directly contains inline namespaces, those inline namespaces are also included in the set.
因此,class Value
的最内层封闭命名空间是 Json
。在 Value
的范围内没有这样的可用函数,命名空间 Json
也类似,因此你遇到了那个错误。
如何解决?
- 将您提供的所有函数移动到命名空间
Json
。
- 将
namespace Json
修改为inline namespace Json
。
拿一个就好了
#include <iostream>
#include <vector>
namespace Cactus
{
namespace Json
{
class Value
{
public:
Value(){}
};
}
typedef std::vector< int > vectorInt;
inline Cactus::Json::Value& operator << (Cactus::Json::Value& os, const vectorInt& v)
{
return os;
}
inline Cactus::Json::Value& operator >> (Cactus::Json::Value& is, vectorInt& v)
{
return is;
}
}
namespace App
//namespace Cactus
{
void f()
{
Cactus::vectorInt ints;
Cactus::Json::Value val;
val << ints;
}
}
int main()
{
App::f();
}
遇到错误,
main.cpp: In function 'void App::f()':
main.cpp:38:13: error: no match for 'operator<<' (operand types are 'Cactus::Json::Value' and 'Cactus::vectorInt' {aka 'std::vector<int>'})
38 | val << ints;
| ~~~ ^~ ~~~~
| | |
| | Cactus::vectorInt {aka std::vector<int>}
| Cactus::Json::Value
但是如果使用“命名空间仙人掌”,它会编译。但是我必须使用“namespace App”,有什么建议吗?
更新:问题已解决。 将两个运算符移动到全局命名空间有效。 或者添加使用 Cactus 也可以。
那些运算符重载必须在您的 f
函数所在的范围内。当函数 f
在 Cactus
命名空间中时,这些运算符重载在范围内。但是当它在命名空间 App
下时,那些就超出了范围。因此,要么将这些运算符重载放在全局命名空间中,要么将运算符重载放在与 class Value
相同的命名空间中,或者将使用该运算符的所有函数放在相同的命名空间中。另一种方法是在函数内部使用 using
语句,使用它们将它们导入到该函数的全局范围。
它被称为ADL
。至少,在您的示例中,函数 Cactus::Json::Value& operator << (Cactus::Json::Value& os, const vectorInt& v)
无法在参数类型 val
的关联 classes 或名称空间中找到。因为 ADL
应受以下规则约束:
For each argument type T in the function call, there is a set of zero or more associated namespaces and a set of zero or more associated classes to be considered. The sets of namespaces and classes are determined entirely by the types of the function arguments (and the namespace of any template template argument). Typedef names and using-declarations used to specify the types do not contribute to this set. The sets of namespaces and classes are determined in the following way:
If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. [...]
If an associated namespace is an inline namespace, its enclosing namespace is also included in the set. If an associated namespace directly contains inline namespaces, those inline namespaces are also included in the set.
因此,class Value
的最内层封闭命名空间是 Json
。在 Value
的范围内没有这样的可用函数,命名空间 Json
也类似,因此你遇到了那个错误。
如何解决?
- 将您提供的所有函数移动到命名空间
Json
。 - 将
namespace Json
修改为inline namespace Json
。
拿一个就好了