我在这里提出的问题之后问这个问题.
这一点非常简单.假设你有两类这样的类:
template < class Derived >
class Base {
...
operator const Derived&() const {
return static_cast< const Derived& >(*this);
}
...
};
class Specialization : public Base<Specialization> {
...
};
Run Code Online (Sandbox Code Playgroud)
然后假设您有类似这样的类型转换:
template < class T >
functionCall( const Base<T>& param) {
const T & val(param);
...
}
Run Code Online (Sandbox Code Playgroud)
问题是:这种转换的标准符合行为应该是什么?
它是否应该相同const T & val(static_cast<const T &> (param) )或应该递归迭代直到堆栈溢出?请注意,我获得了第一个使用GNU编译的行为g++,第二个使用Intel编译icpc.
我已经试图查看标准(关于static_cast的第5.9节和关于转换的第12.3节),但由于我缺乏经验,我无法找到答案.
我非常感谢任何花时间帮助我解决这个问题的人.
我只是想澄清C++是如何工作的,这不是关于解决我的代码中的特定问题.
在C++中,您可以说类型A应该以两种不同的方式隐式转换为类型B.
如果您是A的作者,可以在A中添加这样的内容:
operator B() {
// code
}
Run Code Online (Sandbox Code Playgroud)
如果您是B的作者,可以向B添加这样的内容:
B(const A &a) {
// code
}
Run Code Online (Sandbox Code Playgroud)
如果我理解正确,其中任何一个都将允许A隐式转换为B.所以,如果两个都被定义使用哪一个?这甚至是允许的吗?
注意:我知道您可能永远不会遇到这种情况.您可以使构造函数显式,也可以只使用两者中的一个.我只是想知道C++规范说的是什么,我不知道如何看待它.
在下面的程序中 structS提供了两个转换运算符: indouble和 in long long int。然后一个类型的对象S被传递给一个函数f,重载为floatand double:
struct S {
operator double() { return 3; }
operator long long int() { return 4; }
};
void f( double ) {}
void f( float ) {}
int main() {
S s;
f( s );
}
Run Code Online (Sandbox Code Playgroud)
MSVC 编译器可以正常接受程序,并选择f( double )重载。f然而,GCC 和 Clang 都发现, demo的调用存在歧义:https: //gcc.godbolt.org/z/5csd5dfYz
看来MSVC在这里是对的,因为转换:
operator long long int()->f( float )不是提升。这是错的吗? …
c++ overloading conversion-operator language-lawyer overload-resolution
我最近阅读(并且遗憾地忘记了在哪里),编写operator =的最佳方法是这样的:
foo &operator=(foo other)
{
swap(*this, other);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
而不是这个:
foo &operator=(const foo &other)
{
foo copy(other);
swap(*this, copy);
return *this;
}
Run Code Online (Sandbox Code Playgroud)
我们的想法是,如果使用rvalue调用operator =,则第一个版本可以优化复制的构造.因此,当使用rvalue调用时,第一个版本更快,当使用左值调用时,这两个版本是等效的.
我很好奇其他人对此的看法?由于缺乏明确性,人们会避免使用第一个版本吗?我是否正确,第一个版本可以更好,永远不会更糟?
有一个我无法阻止编译的不受欢迎的 C 样式转换。不需要的强制转换执行从某个类的对象到某个其他类的非常量引用的 C 样式转换。类是不相关的。同时,我喜欢支持从同一个类的对象到 const 引用的 C 风格转换。我正在提供一个公共转换运算符来支持理想的演员阵容。在这种情况下,似乎无法阻止不受欢迎的演员。
对非常量引用的转换无法构建(“Sandbox::B::operator Sandbox::A &() ”(在第 30 行声明)不可访问*),不幸的是,转换为 const 引用要么失败(错误:多个从“Sandbox::B”到“const Sandbox::A”的转换函数适用:函数“Sandbox::B::operator const Sandbox::A &()”函数“Sandbox::B::运算符沙箱::A &()" ):
#include <iostream>
#include <string>
#include <cstdlib>
namespace Sandbox {
class A {
public:
A (int i) : _x (i) { }
private:
int _x;
};
class B {
public:
B (const char* m) : _m (m), _a (std::atoi (m)) { }
/*
* This one shall be supported.
*/
operator const A& () …Run Code Online (Sandbox Code Playgroud) 在定义转换运算符时,定义有什么好处吗?
operator T() const;
Run Code Online (Sandbox Code Playgroud)
超过
operator T&();
operator const T&() const;
Run Code Online (Sandbox Code Playgroud)
假设我不关心返回值而不是引用的潜在性能提升。
我不得不做类似的代码:
#include <type_traits>
template<typename S>
struct probe {
template<typename T, typename U = S, std::enable_if_t<
std::is_same<T&, U>::value &&
!std::is_const<T>::value, int> = 0>
operator T& () const;
template<typename T, typename U = S&&, std::enable_if_t<
std::is_same<T&&, U>::value &&
!std::is_const<T>::value, int> = 0>
operator T&& ();
template<typename T, typename U = S, std::enable_if_t<
std::is_same<T const&, U>::value, int> = 0>
operator T const& () const;
template<typename T, typename U = S&&, std::enable_if_t<
std::is_same<T const&&, U>::value, int> = 0>
operator T const&& () const;
}; …Run Code Online (Sandbox Code Playgroud) 在N4659中16.3.3.1隐式转换序列说
10如果存在几个不同的转换序列,每个转换序列都将自变量转换为参数类型,则与参数关联的隐式转换序列被定义为唯一的转换序列,称为歧义转换序列。为了对16.3.3.2中所述的隐式转换序列进行排名,歧义转换序列被视为与其他任何用户定义的转换序列都没有区别的用户定义的转换序列[注意:此规则可防止函数变为非-viable,因为其参数之一的转换顺序不明确。]如果选择使用歧义转换顺序的函数作为最佳可行函数,则该调用将格式错误,因为该调用中参数之一的转换是模棱两可的。
(当前草案的相应部分为12.3.3.1)
该规则的预期目的是什么,以及引入的歧义转换序列的概念是什么?
文本中提供的注释指出,此规则的目的是“防止功能由于其参数之一的转换顺序不明确而变得不可行 ”。嗯...这实际上是指什么?可行功能的概念在文档的前面部分中定义。它根本不取决于转换的歧义性(每个参数的转换必须存在,但不必明确)。而且似乎没有提供可行的功能以某种方式在以后“变得不可行”的规定(既不是出于某种模棱两可,也不是因为其他原因)。列举了可行的功能,它们相互竞争,成为“最佳”如果有一个“获胜者”,则解决方案成功。在此过程中的任何时候,可行的功能都可能(或需要)变成不可行的功能。
上述段落中提供的示例不是很有启发性(即尚不清楚上述规则在该示例中扮演什么角色)。
这个简单的例子最初引发了这个问题
struct S
{
operator int() const { return 0; };
operator long() const { return 0; };
};
void foo(int) {}
int main()
{
S s;
foo(s);
}
Run Code Online (Sandbox Code Playgroud)
让我们在这里机械地应用上述规则。foo是可行的功能。从参数类型S到参数类型有两个隐式转换序列int:S -> int和S -> long -> int。这意味着,根据上述规则,我们必须将它们“打包”成单个模糊的转换序列。然后我们得出结论,这foo是最好的可行功能。然后我们发现它使用了我们不明确的转换序列。因此,根据上述规则,代码格式错误。
这似乎没有任何意义。这里的自然期望是,S …
c++ conversion-operator language-lawyer overload-resolution implicit-conversion
下面的代码编译罚款既克++ 9.1和铛8.0.0(编译标志是-std=c++17 -Wall -Wextra -Werror -pedantic-errors),但不与MSVC 19.22(编译标志是/std:c++17 /permissive-):
struct X{};
struct Bar
{
Bar() = default;
Bar(X){}
};
struct Foo
{
operator X() const
{
return X{};
}
operator Bar() const
{
return Bar{};
}
};
int main()
{
Foo foo;
[[maybe_unused]]Bar b1 = foo; // OK
[[maybe_unused]]Bar b2(foo); // failed
}
Run Code Online (Sandbox Code Playgroud)
MSVC编译错误:
<source>(27): error C2668: 'Bar::Bar': ambiguous call to overloaded function
<source>(8): note: could be 'Bar::Bar(Bar …Run Code Online (Sandbox Code Playgroud) c++ initialization conversion-operator language-lawyer c++17
我有一个包装器类,其行为应类似于指针。我已经过载operator T*和operator bool。布尔进行了一些额外的验证。我尝试在if中使用该对象,但我注意到该operator T*对象称为和bool。有人可以解释我为什么吗?是在标准中以某种方式指定的吗?我在MSVC,clang和gcc中测试了以下示例代码,它们都调用operator T*。另外,根据我在此页面上阅读的内容(https://en.cppreference.com/w/cpp/language/implicit_conversion),if应该尝试转换为bool。
#include <stdio.h>
class MyClass
{
public:
MyClass(int i)
: m(i)
{}
operator bool() const
{
printf("operator bool()\n");
return m;
}
operator int* ()
{
printf("operator int* ()\n");
return &m;
}
private:
int m;
};
int main()
{
MyClass a(5);
MyClass b(0);
if (a)
printf("a is true\n");
else
printf("a is false\n");
if (b)
printf("b is true\n");
else
printf("b is false\n");
return 0; …Run Code Online (Sandbox Code Playgroud) c++ class operator-overloading conversion-operator implicit-conversion
c++ ×10
standards ×2
c++17 ×1
casting ×1
class ×1
crtp ×1
operators ×1
optimization ×1
overloading ×1
parameters ×1
reference ×1
static-cast ×1
templates ×1