Связывание статической переменной класса без включения объявления всего класса - PullRequest
0 голосов
/ 09 ноября 2018

Предположим, у меня есть исходный файл A.cpp:

#include<string>

struct A {
  static const std::string a;
  static const std::string b;
  static const std::string c;
};

const std::string A::a{"1"};
const std::string A::b{"2"};
const std::string A::c{"3"}; 

И хотите использовать A::b в другой единице перевода. Обычный способ - это разделить A.cpp на объявление A.hpp и определение, включая первое.

#include<iostream>
#include<A.hpp>

int main(){
  std::cout << A::b << "\n";
  return 0;
}

Это, безусловно, сработает, но я хочу не включать объявление класса, потому что в моем случае A огромен и имеет громоздкие зависимости.

В идеале я хочу что-то вроде

struct A; 
external const std::string A::m; 

Но это приводит к ошибке неполного типа.

Можно полагаться на ссылки через другие не классовые глобальные переменные.

const std::string A::b{"2"};
const std::string* bptr{&A::b};

и объявить его в другом исходном файле как

extern const std::string* bptr;

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

Еще один трюк, который работает для меня.

struct A {
  static const std::string b;
};

int main(){
  std::cout << A::b << "\n";
  return 0;
}

Выглядит хорошо, но хакерски, работает даже для частных пользователей. Это законно? То есть, есть что-нибудь, что определяет такое поведение?

Ответы [ 3 ]

0 голосов
/ 09 ноября 2018

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

Одно правило определения. На самом деле нужно рассмотреть две сущности: строку и класс.

Строка полностью соответствует одному правилу определения. Это смутило меня. Это не сильно отличается от объявления одной и той же глобальной переменной в разных источниках. У линкера никогда не возникает проблем, если есть другие ссылки на то же определение. Такое двойное объявление не делает ничего особенного, но предоставляет две ссылки на одно и то же определение. И да, компоновщик не заботится о разных модификаторах доступа разных объявлений.

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

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

И, возможно, я буду придерживаться решения, предложенного Квентином, обеспечивающего доступ через дополнительную функцию.

0 голосов
/ 09 ноября 2018

Если A содержит только статические переменные-члены, то вы можете изменить A из структуры в пространство имен (изменить struct на namespace и избавиться от static перед всеми имена переменных). Затем вы можете разделить «A.h» на несколько заголовочных файлов, чтобы вы могли просто включить нужные вам части. Определения всех переменных могут быть в одном файле .CPP или могут быть разделены между несколькими.

0 голосов
/ 09 ноября 2018

Уловка, которую вы показываете в конце, является, вероятно, худшим взломом и может вызвать неприятные последствия в один прекрасный день - поскольку она «затмевает» другое определение. Здесь нет «промежуточного» (кроме многочисленных «хаков») - либо вы заставляете определение класса отображаться целиком, либо вы снимаете определение строки с класса.

Выглядит хорошо, но хакерски, работает даже для частных пользователей. Это законно?

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...