Использование члена класса в качестве аргумента по умолчанию для функции-члена

Есть ли другой способ, чем вручную перегрузить соответствующую функцию-член и вызвать первую перегрузку с членом в качестве аргумента?

Я пытаюсь что-то сделать в строках

class test
{
    string t1="test";

    testfun( string& val = this->t1 )
    { /* modify val somehow */ }
};

(Проверьте это: http://goo.gl/36p4CF)

В настоящее время, я думаю, нет никаких технических причин, почему это не должно работать.

  • Есть ли решение, делающее это так, кроме перегрузки и установки параметра вручную?
  • Почему это не работает, есть ли для этого техническая причина?

Ответ 1

[dcl.fct.default]/8:

Ключевое слово this не должно использоваться в аргументе по умолчанию для функции-члена.

Это частный случай общей проблемы: вы не можете ссылаться на другие параметры в аргументе по умолчанию для параметра. То есть.

void f(int a, int b = a) {} 

Неправильно сформирован. И так будет

class A
{
    int j;
};

void f(A* this, int i = this->j) {}

В основном это то, что компилятор преобразует функцию-член формы void f(int i = j) {} в. Это связано с тем, что порядок оценки аргументов функции и постфиксное выражение (которое составляет аргумент объекта) не определено. [Dcl.fct.default]/9:

Аргументы по умолчанию оцениваются каждый раз при вызове функции. Порядок оценки аргументов функции не указан. Следовательно, параметры функции не должны использоваться по умолчанию аргумент, даже если они не оценены.

Ответ 2

Вы не сказали, чего хотите достичь. Я предполагаю, что вам нужно, чтобы каждый из ваших экземпляров реагировал определенным образом, в зависимости от определенной переменной класса.

Однако, если вам не требуется поведение каждого экземпляра, вы можете использовать статические переменные. Следующие работы:

#include <iostream>
using namespace std;
struct test {
  static string t1;  
  void say(const string &val=t1){
    cout << val << "!" << endl;
  }
};

string test::t1;

int main() {
   cout << "Hello World" << endl; 
   test::t1 = string("asd");
   test a;
   a.say();
   a.say("bla");
   test::t1 = string("blahblah");
   a.say();
   return 0;
}

... что означает, что все объекты класса test будут использовать статическую строку t1 в качестве значения по умолчанию.

Вы можете немного "взломать" и использовать это как уродливый страж:

  void say(const string &val=t1){
    if (&val == &t1) {
      // They are using the default value, so act as you want
    } else {
      // they are providing a value
    }
  }

Ответ 3

Есть ли решение, делающее это таким образом, кроме перегрузки и установки параметра вручную?

Нет, вам понадобится перегрузка, если вы хотите, чтобы аргумент по умолчанию зависел от другого параметра, включая this. Хотя в этом случае это не имеет смысла, поскольку это конструктор, а t1 не существует до его вызова.

Почему это не работает, есть ли для этого техническая причина?

Поскольку порядок оценки аргументов функции не указан. Чтобы разрешить значения параметров в аргументах по умолчанию, вам понадобится гораздо более сложные правила, чтобы гарантировать, что каждый параметр был инициализирован перед использованием.