Простейшее обходное решение, использующее None
>>> def bar(b, data=None):
... data = data or []
... data.append(b)
... return data
...
>>> bar(3)
[3]
>>> bar(3)
[3]
>>> bar(3)
[3]
>>> bar(3, [34])
[34, 3]
>>> bar(3, [34])
[34, 3]
По умолчанию аргументы полностью компилируются. То есть замещение аргументов по умолчанию вместо отсутствующих аргументов выполняется во время компиляции. По этой причине, очевидно, что выбор аргументов по умолчанию для функций-членов не может зависеть от типа dynamic (т.е. времени выполнения) объекта. Он всегда зависит от типа static (т.е. времени компиляции) объекта.
Вызов, который вы написали в своем примере кода, сразу интерпретируется компилятором как bp->print(10)
независимо от чего-либо еще.
В стандарте говорится (8.3.6.10):
. Вызов виртуальной функции (10.3) использует аргументы по умолчанию в объявлении виртуальной функции, определяемой статическим типом указателя или ссылки обозначая объект. Функция переопределения в производном классе не получает аргументы по умолчанию от функции, которую она переопределяет.
blockquote>Это означает, что, поскольку вы вызываете
B
, он использует аргумент по умолчаниюB::print
.
Динамическое связывание использует vpointer и vtable. Однако динамическое связывание применяется только к указателю на функцию. Механизм динамического связывания отсутствует.
Таким образом, аргумент по умолчанию определяется статически во время компилятора. В этом случае он статически определяется типом bp, который является указателем на базовый класс. Таким образом, data = 10 передается как аргумент функции, а указатель функции указывает на функцию класса Derived: D :: print. По сути, он вызывает D :: print (10).
Следующий фрагмент кода и результирующие выходы четко демонстрируют точку: даже если он вызывает функцию члена Derived call Derived :: resize (int), это передает основной аргумент по умолчанию: size = 0.
virtual void Derived :: resize (int) size 0
#include <iostream>
#include <stdio.h>
using namespace std;
#define pr_dbgc(fmt,args...) \
printf("%d %s " fmt "\n",__LINE__,__PRETTY_FUNCTION__, ##args);
class Base {
public:
virtual void resize(int size=0){
pr_dbgc("size %d",size);
}
};
class Derived : public Base {
public:
void resize(int size=3){
pr_dbgc("size %d",size);
}
};
int main()
{
Base * base_p = new Base;
Derived * derived_p = new Derived;
base_p->resize(); /* calling base member function
resize with default
argument value --- size 0 */
derived_p->resize(); /* calling derived member
function resize with default
argument default --- size 3 */
base_p = derived_p; /* dynamic binding using vpointer
and vtable */
/* however, this dynamic binding only
applied to function pointer.
There is no mechanism to dynamic
binding argument. */
/* So, the default argument is determined
statically by base_p type,
which is pointer to base class. Thus
size = 0 is passed as function
argument */
base_p->resize(); /* polymorphism: calling derived class
member function
however with base member function
default value 0 --- size 0 */
return 0;
}
#if 0
The following shows the outputs:
17 virtual void Base::resize(int) size 0
24 virtual void Derived::resize(int) size 3
24 virtual void Derived::resize(int) size 0
#endif
Обычно используются те аргументы по умолчанию, которые видны в определенной области. Вы можете делать (но не должны) напуганные вещи:
#include <iostream>
void frob (int x) {
std::cout << "frob(" << x << ")\n";
}
void frob (int = 0);
int main () {
frob(); // using 0
{
void frob (int x=5) ;
frob(); // using 5
}
{
void frob (int x=-5) ;
frob(); // using -5
}
}
В вашем случае видна подпись базового класса. Чтобы использовать производные аргументы по умолчанию, вы должны явно вызвать эту функцию с помощью указателя на ваш производный класс, либо объявив его таким образом, либо правильно произведя его.
Значение аргумента по умолчанию передается от имени вызывающего. С точки зрения вызывающего абонента он работает с классом B (не D), поэтому он пропускает 10 (как для класса B)
ваша переменная имеет тип B, поэтому вызывается функция B. Чтобы позвонить D, вам нужно либо объявить свою переменную как D, либо записать в D.
print
объявляется виртуальной, вызов через указатель базового класса вызовет производную версию функции-члена, а не базовую версию.
– templatetypedef
29 June 2017 в 22:55