Возможно ли обеспечить во время компиляции, чтобы два производных класса всегда возвращали разные значения для переопределяющей функции? - PullRequest
3 голосов
/ 04 мая 2020

Можно ли принудить во время компиляции принять следующее:

class B {
public:
    virtual constexpr const char* getKeyStr() const = 0;
};

class D1 : public B {
public:
    constexpr const char* getKeyStr() const override { return "D1"; }
};

class D2 : public B {
public:
    constexpr const char* getKeyStr() const override { return "D2"; }
};

... Но следующее не так? Мы не хотим, чтобы D1 и D2 возвращали одну и ту же строку ключа:

class B {
public:
    virtual constexpr const char* getKeyStr() const = 0;
};

class D1 : public B {
public:
    constexpr const char* getKeyStr() const override { return "D1"; }
};

class D2 : public B {
public:
    constexpr const char* getKeyStr() const override { return "D1"; } // can we error out here at compile time?
};

Пояснения:

  1. Я показываю только два производных класса в в этом примере, но я пытаюсь достичь этого - разместить это ограничение на любом количестве производных классов.

  2. Основная проблема, которую необходимо решить: я думаю о сериализации / десериализации приложения, где каждый объект с одним и тем же базовым классом сможет сгенерировать свое текстовое / строковое представление для записи в файл и, если ему будет возвращена эта строка (назовем ее строкой содержимого), сможет восстановить соответствующую data.

    Во время десериализации код приложения должен уметь определять из ключевой части строки содержимого, назовем его «строка ключа», какой производный объект следует реконструировать. Следовательно, строка ключа должна быть уникальной для каждого производного класса. Я знаю, что type_info::name может быть уникальным, но его нельзя настроить (или не зависит от компилятора?).

  3. Относительно функции getKeyStr() (соответствует указанной выше ключевой строке), он всегда должен возвращать одну и ту же строку для всех объектов одного и того же производного класса.

Ответы [ 2 ]

2 голосов
/ 04 мая 2020

Да, вы можете проверить во время компиляции, отличаются ли «строки», возвращаемые getKeyStr от D1 и D2.

Сначала предоставьте функцию, которая сравнивает 2 const char * во время компиляции:

constexpr bool different(const char *x, const char *y)
{
  while(*x != '\0' )
    if (*x++ != *y++) return true;

  return *y != '\0'; 
}

, а затем сравнивает возвращаемые значения:

// this will trigger, if returned strings are the same.
static_assert( different(D1{}.getKeyStr(), D2{}.getKeyStr()) );  

Редактировать: As @ Jarod42 указывает, что string_view сравнение равно constexpr, и поэтому функцию сравнения можно написать гораздо проще:

constexpr bool different(const char * x, const char * y)
{
    return std::string_view{x} != std::string_view{y};
}

Вот демо .

1 голос
/ 04 мая 2020

Если у вас есть список всех производных классов, вы можете сделать что-то вроде:

template <typename... Ts>
bool have_unique_keys()
{
    std::string_view keys[] = {Ts{}.getKeyStr()...}; // requires constexpr default constructor
                                                     // static method might be more appropriate
    std::sort(std::begin(keys), std::end(keys)); // constexpr in C++20
    auto it = std::adjacent_find(std::begin(keys), std::end(keys));
    return it != std::end(keys);
}

static_assert(have_unique_keys<>(D1, D2, D3));
...