Разрешено ли классам иметь разные определения для разных единиц перевода в программе? - PullRequest
3 голосов
/ 09 января 2020

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

Вариант использования: доступ к деталям реализации без динамического выделения c. Код C ++ будет работать с указателями, уже выделенными библиотекой C.

Пожалуйста, игнорируйте утечки памяти для примера.

common.hpp

#pragma once

namespace Test {

class Impl;

class A {
    void *ptr;
    A(void *ptr) : ptr(ptr) {}
    friend class Impl;

   public:
    int plus_one();
};

class B {
    void *ptr;
    B(void *ptr) : ptr(ptr) {}
    friend class Impl;

   public:
    int plus_two();
};

class Factory {
   public:
    A getA(int val);
    B getB(int val);
};

}  // namespace Test

A . cpp

#include "common.hpp"

namespace Test {

class Impl {
   public:
    static int as_int(A *a) { return *static_cast<int *>(a->ptr) + 1; }
};

int A::plus_one() { return Impl{}.as_int(this); }

}  // namespace Test

B. cpp

#include "common.hpp"

namespace Test {

class Impl {
   public:
    static int as_int(B *b) { return *static_cast<int *>(b->ptr) + 2; }
};

int B::plus_two() { return Impl{}.as_int(this); }

}  // namespace Test

Factory. cpp

#include "common.hpp"

namespace Test {

class Impl {
   public:
    static A getA(int val) { return A(new int{val}); }
    static B getB(int val) { return B(new int{val}); }
};

A Factory::getA(int val) { return Impl{}.getA(val); }
B Factory::getB(int val) { return Impl{}.getB(val); }

}  // namespace Test

main. cpp

#include <iostream>

#include "common.hpp"

int main() {
    Test::Factory factory;
    std::cout << factory.getA(1).plus_one() << std::endl;
    std::cout << factory.getB(1).plus_two() << std::endl;
    return 0;
}

Выход:

$ g++ A.cpp B.cpp Factory.cpp main.cpp -o test 
$ ./test
2
3

1 Ответ

4 голосов
/ 09 января 2020

Нет, один и тот же тип класса не может иметь разные определения. Ваша программа является прямым нарушением ODR и демонстрирует неопределенное поведение.

[basi c .def.odr]

6 В программе может быть несколько определений типа класса [...] при условии, что каждое определение отображается в другой единице перевода, и при условии, что определения удовлетворяют следующим требованиям. Если такой объект с именем D определен более чем в одной единице перевода, то

  • , каждое определение D должно состоять из одной и той же последовательности токенов; и
  • [...]

[...] Если определения D удовлетворяют всем этим требованиям, то поведение такое, как если бы существовало одно определение D. Если определения D не удовлетворяют этим требованиям, то поведение не определено.

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

...