как да създадете конструктор за преместване, който приема const char* низов литерал

Искам да създам конструктор за преместване, който приема низов литерал, и след това да преместя този c низ към членен указател. Най-доброто решение, което мога да напиша, е да дам предупреждение: отхвърлено преобразуване от низова константа в 'char*' [-Wwrite-strings] CTextBlock cctb("move(H)"); ^ кодът:

#include <iostream>
using namespace std;

class CTextBlock
{
public:
    CTextBlock(char* &&text)//move constructor
    {
        pText = text;
    }
private:
    char *pText;
};
int main()
{
    CTextBlock cctb("move(H)"); //WARNING
    return 0;
}

person Obay Abd-Algader    schedule 11.04.2015    source източник
comment
Това не е конструктор на движение. Това е просто обикновен конвертиращ конструктор.   -  person chris    schedule 12.04.2015
comment
как , отнема char* &&   -  person Obay Abd-Algader    schedule 12.04.2015
comment
Конструкторът на движение би отнел CTextBlock&&. Изобщо не виждам смисъла от препратката към rvalue тук. Това, което имате, е проблемът да се опитвате да посочите постоянни данни, като използвате указател към непостоянни данни.   -  person chris    schedule 12.04.2015
comment
Опитайте template<size_t N>CTextBlock(char const(&&text)[N]) и използвайте std::move на литерала "". Не съм сигурен дали ще работи, но опитайте. О, и направете низовете член char const*: "literal" да са постоянни данни.   -  person Yakk - Adam Nevraumont    schedule 12.04.2015
comment
искаш да кажеш, че това, че приема препратка към rvalue, не го прави конструктор на движение?   -  person Obay Abd-Algader    schedule 12.04.2015
comment
@ObayAbd-Algader: Не всеки конструктор, който приема T&&, е конструктор за движение. Това е само случаят, когато е конструктор на T!!   -  person Lightness Races in Orbit    schedule 12.04.2015
comment
какво е template‹size_t N›CTextBlock(char const(&&text)[N])?   -  person Obay Abd-Algader    schedule 12.04.2015
comment
добре, сработи @chris, но не искам членът да сочи към const   -  person Obay Abd-Algader    schedule 12.04.2015
comment
Това е конструктор, вземащ препратка към rvalue към масив от N обекти от тип char const. Например низов литерал има този тип. Защо се предава препратка към rvalue на такъв refecens, не знам и всъщност не мисля, че работи: резултатът от std::move("foo") should be a const` rvalue на такъв масив...   -  person Dietmar Kühl    schedule 12.04.2015
comment
Ако не искате членът да бъде char const*, вероятно ще трябва да копирате низовия литерал, тъй като символите в низовия литерал са const.   -  person Dietmar Kühl    schedule 12.04.2015


Отговори (3)


Първо, типът на низовите литерали е char const[N] (за подходяща константа N). Този масив може да бъде присвоен на char const*, в който случай ще се разпадне в указател към първия елемент. Той не може да бъде преобразуван в char*. Преди C++11 на преобразуването към char* беше позволено да работи със съществуващ код, който не беше const-правилен (напр. защото започна като C код, преди C да получи const). Това преобразуване беше премахнато за C++11.

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

Във вашия въпрос вие посочвате, че искате да създадете конструктор за преместване, но конструкторите за преместване вземат препратка към rvalue на класа, за който са, например това ще бъде конструктор за преместване за вашия клас:

CTextBlock::CTextBlock(CTextBlock&& other)
    : pText(other.pText) {
    other.pText = 0;
}

(вашият клас не показва никаква семантика на собственост за указателя pText, в който случай конструкцията на преместване всъщност няма много смисъл; горният код предполага, че има някаква семантика на собственост и че нулев указател показва, че обектът не притежава нещо).

Това, че даден аргумент е ограничен да бъде препратка към rvalue, не означава, че тази функция е конструктор на движение. Всичко, което предполага, е, че аргументът е rvalue и разумно може да се приеме, че текущото му представяне не е необходимо да се запазва. Низовият литерал изглежда е rvalue, защото низовият литерал се преобразува във [временен] указател към началото на масива.

person Dietmar Kühl    schedule 11.04.2015

Конструктор, който сравнително се доближава до разрешаването само на литерали, може да бъде реализиран като шаблон:

#include <cstddef>
#include <assert>

struct X
{
    char const * str;
    std::size_t len;

    template <std::size_t N>
    X(char const (&a)[N]) : str(a), len(N - 1)
    {
        assert(a[len] == '\0');   // true for string literals
    }
};

Това обаче не е безупречно, тъй като ще се свърже и с наименувани символни масиви, а изчисляването на дължината е съмнително, ако вашият низ също съдържа нулеви стойности. Но ако се опитвате да избегнете случайно използване на динамични стойности (например в алгоритъм, който изгражда низове от изрази и литерални низове), това е доста полезно.

person Kerrek SB    schedule 11.04.2015
comment
=delete за не-const подобрява това. - person Yakk - Adam Nevraumont; 12.04.2015

Низовият литерал е const char *. Това е вярно, независимо дали се използва или не като lvalue или rvalue.

Ако прегледате кода си отново, вие следователно се опитвате да съхраните const char * в char * и оттам идва вашата диагностика на компилатора. Вашият конструктор на преместване взема препратка към rvalue към char *, а не към const char *. Променете го на const char * и променете члена на класа pText на const char * и това, което се опитвате да направите, трябва да работи.

person Sam Varshavchik    schedule 11.04.2015
comment
Низов литерал е const char * Не, не е. - person Lightness Races in Orbit; 12.04.2015