връщане на низ от функция

Исках да напиша функция, която ще бъде междуплатформена (win32 и linux) и ще върне низово представяне на датата и часа [чч:мм:сс дд-мм-гггг].

Знаейки, че просто искам да използвам върнатия низ като временен по потоков начин, както е показано по-долу:

std::cout << DateTime() << std::endl;

Обмислях да напиша функция със следния прототип

const char* DateTime();

Ако върнете масив от знаци, трябва да го изтриете, след като сте готови. Но аз просто искам временно, не искам да се притеснявам за премахване на разпределението на низа.

Затова написах функция, която просто връща std::string:

#include <ctime>
#include <string>
#include <sstream>

std::string DateTime()
{
    using namespace std;

    stringstream ss;
    string sValue;
    time_t t = time(0);
    struct tm * now = localtime(&t);

    ss << now->tm_hour << ":";
    ss << now->tm_min << ":";
    ss << now->tm_sec << " ";
    ss << now->tm_mday + 1 << " ";
    ss << now->tm_mon + 1 << " ";
    ss << now->tm_year + 1900;

    sValue = ss.str();

    return sValue;
}

Осъзнавам, че връщам копие на стековата променлива в DateTime. Това е неефективно, тъй като създаваме низа в стека DateTime, попълваме го, след което връщаме копие и унищожаваме копието в стека.

Революцията на семантиката на движение на c++11 направи ли нещо, за да разреши тази неефективност - мога ли да подобря това?


person fishfood    schedule 29.06.2012    source източник
comment
1) NRVO прави това напълно безпроблемно. 2) Ако NRVO не се включи по някаква причина, тогава да, върнатата стойност ще бъде преместена, а не копирана.   -  person ildjarn    schedule 30.06.2012
comment
разгледайте: stackoverflow.com/a/3109981/484072   -  person peacemaker    schedule 30.06.2012
comment
en.wikipedia.org/wiki/Return_value_optimization   -  person anio    schedule 30.06.2012
comment

Наистина не виждам нужда от всички тези неща.

Защо просто не установите връзка със сървъра по време на проверката, сървърът да генерира файл с всички параметри, които искате в него (дори в обикновен текст) и след това сървърът да подпише този файл и да го върне на софтуера?

Клиентът може да прочете файла и след това просто да потвърди подписа, като използва един от многото налични алгоритми за цифров подпис.

  -  person Jerry Coffin    schedule 30.06.2012
comment
като други казаха, че RVO е гарантирано, ако използвате C++11, но не бих се притеснявал, защото потокът от низове ще бъде тясното гърло тук.   -  person 111111    schedule 30.06.2012
comment
@111111 : RVO никога не е гарантирано в стандарта (98, 03 или 11), просто е разрешено. От изпълнението зависи да даде гаранции по отношение на тази конкретна оптимизация.   -  person ildjarn    schedule 30.06.2012
comment
tm_mday + 1 - трябва да премахнете това -1, tm_mday е базирано на 1.   -  person Alexey Frunze    schedule 30.06.2012
comment
Бях изненадан, че никой друг не забеляза това!   -  person fishfood    schedule 30.06.2012


Отговори (4)


lapin, кодът ти е добър C++11 код. В C++98/03 вашият код вероятно ще бъде ефективен поради оптимизации на компилатора, но тези оптимизации не са гарантирани. В C++11 същите тези оптимизации вероятно пак ще направят връщането ви безплатно, но в случай, че не го направят, вашият низ ще бъде преместен вместо копиран.

Така че връщайте по стойност без вина! :-)

Дребна гнида:

Най-добрата практика е да декларирате вашите стойности в точката на първото използване, вместо в горната част на блок:

string sValue = ss.str();
return sValue;

Или може би дори:

return ss.str();

Но това е само малка гнида. Вашият код е добър и ефективен.

person Howard Hinnant    schedule 30.06.2012
comment
Започнах (лошия?) навик да декларирам моите променливи преди моя код, когато пишех твърде много MSSQL съхранени процедури преди няколко години. Чувал съм мнението ви преди за декларирането на променливи в точката на първо използване за ефективност и съм съгласен с него, но все пак се оказва, че декларирам преди кода :) - person fishfood; 01.07.2012

Здравей Рик. Примерният XML, който показах, е изход. Входният XML, който анализира добре, няма префикса „ns0:“. Това е просто стандартен GPX код.
person Nevin    schedule 30.06.2012

Добре, знам, че това не е безопасно за нишки и всичко това и вероятно ще бъда отрицателен до края, но видях следното в библиотека, която използвам (ROOT на CERN):

const char * myfunc(){

  static std::string mystr;

  /*populate mystr */

  return mystr.c_str();
}

Това работи само ако знаете, че никой никога няма да бъде толкова глупав, че да се хване за показалеца.

Това е начин да имате временен, който няма да изтече, независимо какво.

person Simon    schedule 30.06.2012
comment
Това беше бърз DeadMG! :-) Проблемът с този отговор е, че той извиква недефинирано поведение. Докато клиентът изпрати указателя към cout, локалният mystr вече е изтрил масива от знаци, към който сочи този указател. Може да проработи и не се съмнявам, че Саймън е тествал преди да публикува. Но също така може да не работи. В многонишкова среда едно и също приложение понякога може да работи, а понякога не. - person Howard Hinnant; 30.06.2012
comment
@Howard : mystr е статичен, така че тук няма разрушаване; Мисля, че гнидата на DeadMG е, че това не е безопасно за нишки. - person ildjarn; 30.06.2012
comment
@ildjarn: Така е! Благодаря за корекцията. Напълно го пренебрегнах. Каква е конвенцията тук? Да изтрия коментара ми, защото може да обърка хората, или да го оставя, за да могат да прочетат и този? - person Howard Hinnant; 30.06.2012
comment
@Howard : Вашето обаждане; това може да обърка хората или може да бъде изясняващо за други читатели, които правят същата грешка, която сте направили при четенето на кода. :-] - person ildjarn; 30.06.2012

В свят без RVO/NRVO това трябва да избягва конструкцията на копиране в стандартна библиотека преди C++11. В пост C++11 библиотека с конструктор за преместване за низ все още избягва извикването на конструктора за преместване; вероятно това е тривиално малка разлика, но все пак ОП питаше как да се направи по-добре.

(И да, съгласен съм, че наследяването от низ е грозно, но работи.)

#include <ctime>
#include <string>
#include <sstream>
#include <iostream>

using namespace std;

class DateString : public string {

public:
DateString() : string()     {

    stringstream ss;
    time_t t = time(0);
    struct tm * now = localtime(&t);

    ss << now->tm_hour << ":";
    ss << now->tm_min << ":";
    ss << now->tm_sec << " ";
    ss << now->tm_mday + 1 << " ";
    ss << now->tm_mon + 1 << " ";
    ss << now->tm_year + 1900;

    append(ss.str());

}
};

int main()
{
    cout << DateString() << endl;
    return 0;
}
person Pete Fordham    schedule 29.06.2012
comment
1) Наследяването от std::string е просто (морално) погрешно. 2) Кодът, който OP вече има, също ще избегне изграждането на копие дори без NRVO. - person ildjarn; 30.06.2012