Как отключить копирование для конструкторов с помощью std::source_location или обойти проблему?
Я пытаюсь добавить инструментарий к широко используемому классу шаблонов в моем продукте. Я сейчас на
VS 2019 (16.10.4)
с участием
/std:c++17
. Новая функция - отличное дополнение для задач, в решении которых я заинтересован. Пока и
std::experimental::source_location
недоступны в моем компиляторе, я построил свой собственный на основе этого ответа . Моя цель - внести изменения в конструктор / члены класса в специальные тесты сборки и запуска. Изменения самого класса не влияют на его использование, поэтому остальная часть кода остается прежней.
Все это компилируется и отлично работает - в основном. За исключением того, что я сталкиваюсь с копией, которая почти превосходит цель использования
std::source_location
. Эта проблема может быть продемонстрирована на
gcc -std=c++20
с и без
-fno-elide-constructors
также. См. Упрощенную версию моего минимального воспроизводимого образца Godbolt .
Мои занятия:
class MyClass
{
private:
int m_a = 0;
std::source_location m_location = std::source_location::current();
public:
MyClass(std::source_location location = std::source_location::current())
: m_location(location)
{
}
MyClass(const MyClass &other, std::source_location location = std::source_location::current())
: m_a(other.m_a)
, m_location(location)
{
}
MyClass(MyClass &&other, std::source_location location = std::source_location::current())
: m_a(other.m_a)
, m_location(location)
{
}
};
Использование:
int main()
{
MyClass o1;
MyClass o2(o1);
MyClass o3(getCopy1());
MyClass o4(getCopy2());
std::cout << "o1: " << o1.getLocationInfo() << std::endl;
std::cout << "o2: " << o2.getLocationInfo() << std::endl;
std::cout << "o3: " << o3.getLocationInfo() << std::endl;
std::cout << "o4: " << o4.getLocationInfo() << std::endl;
return 0;
}
Фактический выход:
o1: /app/example.cpp(56:13) int main()
o2: /app/example.cpp(57:18) int main()
o3: /app/example.cpp(46:12) MyClass getCopy1()
o4: /app/example.cpp(51:20) MyClass getCopy2()
Ожидаемый результат:
o1: /app/example.cpp(56:13) int main()
o2: /app/example.cpp(57:18) int main()
o3: /app/example.cpp(58:26) int main()
o4: /app/example.cpp(59:26) int main()
Текущее поведение соответствует тому, что мы видим для конструкторов с завершающими аргументами по умолчанию. См. Образцы царапин .
1 ответ
Вы можете попробовать что-то вроде этого:
template <int line, std::size_t N, basic_fixed_string<N> file>
class MyClassImpl {
public:
// copy from any instantiation
template <int line2, std::size_t N2, basic_fixed_string<N2> file2>
MyClassImpl(const MyClassImpl<line2, N2, file2>& other) { ... }
// also move, assignment etc
...
};
#define MyClass MyClassImpl<__LINE__, sizeof(__FILE__)-1, __FILE__>
Ты можешь взять
basic_fixed_string
от сюда или свернуть свой собственный; также не стесняйтесь использовать свою реализацию
std::source_location
вместо старых макросов, если это возможно. Идея состоит в том, чтобы каждое упоминание о
MyClass
уникальный тип. Параметры шаблона не используются в определении класса, они могут быть любыми, если они уникальны.
Теперь больше не будет копировать элизию, когда вы это сделаете.
MyClass o3(getCopy1());
потому что типы не совпадают, поэтому нет копии, которую следует исключить.
Конечно, это вообще не работает, если вы используете
auto
:
auto o3 = getCopy1(); // still has copy elision
так что это не надежное решение.