重载分辨率和const的共享指针

时间:2016-12-08 13:11:18

标签: c++ shared-ptr overload-resolution qualifiers

我正在转换大型代码以使用自定义共享指针而不是原始指针。我有重载分辨率的问题。考虑这个例子:

#include <iostream>

struct A {};

struct B : public A {};

void f(const A*)
{
    std::cout << "const version\n";
}

void f(A*)
{
    std::cout << "non-const version\n";
}

int main(int, char**)
{
    B* b;
    f(b);
}

此代码正确写入“非const版本”,因为qualification conversions在隐式转换序列的排名中起作用。现在看一下使用shared_ptr的版本:

#include <iostream>
#include<memory>

struct A {};

struct B : public A {};

void f(std::shared_ptr<const A>)
{
    std::cout << "const version\n";
}

void f(std::shared_ptr<A>)
{
    std::cout << "non-const version\n";
}

int main(int, char**)
{
   std::shared_ptr<B> b;
   f(b);
}

此代码无法编译,因为函数调用不明确。

我知道user-defined deduction-guide将是一个解决方案,但在Visual Studio中仍然不存在。

我正在使用regexp转换代码,因为有数千个此类调用。正则表达式无法区分与const版本匹配的调用与与非const版本匹配的调用。使用共享指针时是否可以更好地控制重载决策,并避免手动更改每个调用?当然我可以.get()原始指针并在调用中使用它但我想完全消除原始指针。

2 个答案:

答案 0 :(得分:1)

你可以引入额外的重载来为你做出爆燃:

template <class T>
void f(std::shared_ptr<T> a)
{
  f(std::static_pointer_cast<A>(a));
}

template <class T>
void f(std::shared_ptr<const T> a)
{
  f(std::static_pointer_cast<const A>(a));
}

您还可以使用std::enable_if来限制非const T的第一次重载,和/或将T的两次重载限制为A。 1}}。

这是如何运作的:

某些std::shared_ptr<X>的{​​{1}}不是X也不是Aconst AB )。如果没有模板重载,编译器必须选择将此const B转换为std::shared_ptr<X>std::shared_ptr<A>。两者都是排名相同的良好转换(两者都是用户定义的转换),因此存在歧义。

添加模板重载后,有四种参数类型可供选择(让我们分析std::shared_ptr<const A>案例):

  1. X = const B
  2. std::shared_ptr<A>
  3. std::shared_ptr<const A>从第一个模板实例化,std::shared_ptr<const B>
  4. T = const B从第二个模板设置std::shared_ptr<const B>
  5. 显然,类型3和4优于1和2,因为它们根本不需要转换。因此将选择其中一个。

    类型3和4本身是相同的,但是随着模板的重载分辨率的增加,还会出现其他规则。即,一个更专业的模板&#34; (更多的非模板签名匹配)优先于一个较不专业的。由于过载4在签名的非模板部分(T = B之外)中有const,因此它更专业,因此被选中。

    没有规则说&#34;模板更好。&#34;事实上,恰恰相反:当模板和非模板具有相同的成本时,非模板是首选。这里的诀窍是模板的成本较小(不需要转换)比非模板(需要用户定义的转换)。

答案 1 :(得分:0)

标签调度可以解决问题 它遵循一个最小的工作示例:

import pandas as pd
from pandas_datareader import data, wb
import datetime

start = datetime.datetime(2016,1,1)
end = datetime.date.today()

apple = data.DataReader(input("Please Input the name of the Ticker:\n"), "yahoo", start, end)

type(apple)
apple.head()

Please Input the name of the Ticker:
AAPL

    Open    High    Low     Close   Volume  Adj Close
Date                        
2016-01-04  102.610001  105.370003  102.000000  105.349998  67649400    103.057063
2016-01-05  105.750000  105.849998  102.410004  102.709999  55791000    100.474523
2016-01-06  100.559998  102.370003  99.870003   100.699997  68457400    98.508268
2016-01-07  98.680000   100.129997  96.430000   96.449997   81094400    94.350769
2016-01-08  98.550003   99.110001   96.760002   96.959999   70798000    94.849671

如您所见,您已经拥有了创建标记所需的内容:存储的指针。
无论如何,你不必得到它并在呼叫点传递它。相反,通过使用中间函数模板,您可以将其用作在内部调度调用的类型。如果您不想使用它,您甚至不必为参数命名。