Ошибка при создании шаблона

I was writing code that pertained to a linkedlist, and I wanted to template the whole program. Here is what I have written:

template<typename T>
class Node
{
public:
    T data;
    Node* next;
    Node(){};
};
class List{
public:Node<T>* head;
List() { head= NULL; } //constructor 

For that, it works just fine with my other functions. However, I am also trying to write a function copy, which copies the list to another.

List Copy(List copyme){
    List<T> x; 
    x = new List<T>;
    Node<T>* current = copyme.head;
    while (current != NULL){
        x.ListInsertHead(current->data);
        current = current->next;
    }
    x.ListReverse();
    return x;
    };

I am receiving errors about templating the class, what should I write in this case? Thanks. The errors are just undeclared identifiers, which happen because I’m templating it wrongly.

Универсальные ссылки (то есть «прямые ссылки», c++ стандартное название) и идеальная пересылка в c++11, c++14и за ее пределами имеют много важных преимуществ; увидеть Вот, а также Вот.

В статье Скотта Мейерса, упомянутой выше (ссылка на сайт), как правило, утверждается, что:

Если переменная или параметр объявлены как имеющие тип T&& для некоторых выведенный тип T, эта переменная или параметр является универсальной ссылкой.

Пример 1

Действительно, используя clang ++, мы видим, что следующий фрагмент кода успешно скомпилируется с -std=c++14:

#include <utility>

template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}

int        x1 = 1;
int const  x2 = 1;
int&       x3 = x1;
int const& x4 = x2;

// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1);    // various lvalues okay, as expected
auto r2 = f (x2);    // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected

Дано любое описание универсальных ссылок (прямые ссылки) и вывода типа (см., Например, это объяснение) понятно, почему вышесказанное работает. Хотя из того же объяснения не совсем понятно, почему нижеприведенное также не работает.

(не удалось) Пример 2

Этот вопрос решает ту же проблему. Однако предоставленные ответы не объясняют, почему шаблонные типы не классифицируются как «выводимые».

То, что я собираюсь показать (по-видимому), удовлетворяет требованиям, изложенным выше Мейерсом. Тем не менее, следующий код снят терпит неудачу компилировать, выдавая ошибку (среди прочего для каждого вызова f):

test.cpp: 23: 11: ошибка: нет подходящей функции для вызова ‘f’

auto r1 = f (x1);

test.cpp: 5: 16: примечание: функция-кандидат [с T = foo, A = int] not
жизнеспособный: нет известного преобразования из ‘struct foo< int> ‘to’ foo< int> &&’
за 1-й аргумент

decltype (авто) f (T)< A> && т)

#include <utility>

//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}

template <typename A>
struct foo
{
A bar;
};

struct foo<int>        x1 { .bar = 1 };
struct foo<int> const  x2 { .bar = 1 };
struct foo<int> &      x3 = x1;
struct foo<int> const& x4 = x2;

// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works

В контексте, так как тип T<A> из fпараметр является вывел, обязательно объявление параметра T<A>&& t будет вести себя как универсальная ссылка (прямая ссылка).

Пример 3 (для ясности в описании проблемы под рукой)

Позвольте мне подчеркнуть следующее: сбой кода в Example 2 компилировать это не благодаря тому факту, что struct foo<> это шаблонный тип. Похоже, причина неудачи только объявлением fПараметр как шаблонный тип.

Рассмотрим следующую редакцию предыдущего кода, которая сейчас делает компиляции:

#include <utility>

//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}

//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};

struct foo<int>        x1 { .bar = 1 };
struct foo<int> const  x2 { .bar = 1 };
struct foo<int> &      x3 = x1;
struct foo<int> const& x4 = x2;

// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);

Меня удивляет, что это простое изменение полностью меняет поведение вывода типа для шаблонной функции fТип параметра.

Вопросы:

Почему второй пример не работает, как ожидалось? Существуют ли способы преодоления этой проблемы с помощью шаблонных типов в c++11/14? Существуют ли хорошо известные существующие кодовые базы (в дикой природе), успешно использующие c++Прямые ссылки с шаблонными типами?

11

Решение

Когда вы вызываете какую-то функцию f с некоторым значением:

int a = 42;
f(a);

затем f должен быть в состоянии принять такое значение. Это тот случай, когда первый параметр f является (lvalue) ссылочным типом, или когда это вообще не ссылка:

auto f(int &);
auto f(int); // assuming a working copy constructor

это не будет работать, когда параметр является rvalue ссылкой:

auto f(int &&); // error

Теперь, когда вы определяете функцию со ссылкой для пересылки в качестве первого параметра, как вы делали в первом и третьем примере …

template<typename T>
auto f(T&&); // Showing only declaration

… и вы на самом деле вызываете эту функцию с lvalue, вычет типа шаблона превращается T в (lvalue) ссылку (то, что это происходит, можно увидеть в примере кода, который я приведу чуть позже):

auto f(int & &&); // Think of it like that

Конечно, здесь слишком много ссылок. Так что C ++ имеет рушатся правила, которые на самом деле довольно просты:

  • T& & становится T&
  • T& && становится T&
  • T&& & становится T&
  • T&& && становится T&&

Благодаря второму правилу, «эффективный» тип первого параметра f является ссылкой на lvalue, так что вы можете привязать ее к lvalue.

Теперь, когда вы определяете функцию g лайк …

template<template<class> class T, typename A>
auto g(T<A>&&);

Тогда несмотря ни на что, вычет параметра шаблона должен повернуть T в шаблон, не тип. Ведь именно это вы указали при объявлении параметра шаблона как template<class> class вместо typename,
(Это важное отличие, foo в вашем примере это не тип, это шаблон … который вы можете видеть как функцию уровня типа, но вернемся к теме)

Сейчас, T это какой-то шаблон. У вас не может быть ссылки на шаблон.
Ссылка (тип) создается из (возможно, неполного) тип. Так что ни на что, T<A> (который является типом, но нет параметр шаблона, который может быть выведен) не превратится в (lvalue) ссылку, что означает T<A> && не нуждается в свертывании и остается тем, что есть: ссылка на значение. И, конечно, вы не можете привязать lvalue к ссылке на rvalue.

Но если вы передадите ему значение, то даже g буду работать.

Все вышеперечисленное можно увидеть в следующем примере:

template<typename X>
struct thing {
};
template<typename T>
decltype (auto) f(T&& t) {
if (std::is_same<typename std::remove_reference<T>::type, T>::value) {
cout << "not ";
}
cout << "a reference" << endl;
return std::forward<T>(t);
}
template<
template<class> class T,
typename A>
decltype (auto) g(T<A>&& t) {
return std::forward<T<A>>(t);
}
int main(int, char**) {
thing<int> it {};

f(thing<int> {}); // "not a reference"
f(it);            // "a reference"// T = thing<int> &
// T&& = thing<int>& && = thing<int>&

g(thing<int> {}); // works

//g(it);
// T = thing
// A = int
// T<A>&& = thing<int>&&

return 0;
}

(Живи здесь)

Относительно того, как можно «преодолеть» это: вы не можете. По крайней мере, не так, как вы этого хотите, потому что естественное решение — третий пример, который вы предоставляете: поскольку вы не знаете переданный тип (это ссылка lvalue, ссылка rvalue или ссылка вообще?), вы должны сохранять его как общий T, Вы, конечно, могли бы обеспечить перегрузки, но это как-то не позволило бы достичь идеальной пересылки, я думаю.


Хм, оказывается, ты на самом деле Можно преодолеть это, используя некоторые черты класса:

template<typename> struct traits {};
template<
template<class>class T,
typename A>
struct traits<T<A>> {
using param = A;
template<typename X>
using templ = T<X>;
};

Затем вы можете извлечь как шаблон, так и тип, с которым был создан экземпляр шаблона внутри функции:

template<typename Y>
decltype (auto) g(Y&& t) {
// Needs some manual work, but well ...
using trait = traits<typename std::remove_reference<Y>::type>;
using A = typename trait::param;
using T = trait::template templ
// using it
T<A> copy{t};
A data;
return std::forward<Y>(t);
}

(Живи здесь)


[…] Можно вы объясните, почему это не универсальная ссылка? Какова будет опасность или подводный камень или ее слишком сложно реализовать? Я искренне заинтересован.

T<A>&& не универсальная ссылка, потому что T<A> не является параметром шаблона. Это (после вычета обоих T а также Aпростой (фиксированный / не универсальный) тип.

Серьезным подводным камнем для ссылки на пересылку было бы то, что вы больше не можете выразить текущее значение T<A>&&: Rvalue ссылка на некоторый тип, созданный из шаблона T с параметром A,

7

Другие решения

Почему второй пример не работает, как ожидалось?

У вас есть две подписи:

template <typename T>
decltype(auto) f (T&& );

template <template <typename> typename T, typename A>
decltype(auto) f2 (T<A>&& );

f принимает ссылку на пересылку, но f2 не. Конкретное правило, из [temp.deduct.call], выделено жирным шрифтом:

экспедиционная ссылка это значение
ссылка на резюме параметр шаблона. Если P является ссылкой для пересылки, а аргумент является
lvalue, тип «lvalue ссылка на A» используется вместо A для вывода типа.

С f, аргумент является ссылкой на параметр шаблонаT). Но с f2, T<A> не является параметром шаблона. Таким образом, эта функция просто принимает в качестве аргумента ссылку на значение T<A>, Вызовы не компилируются, потому что все ваши аргументы являются lvalue, и в этом случае нет особых исключений для вызова с lvalue.

2

Что касается преодоления проблемы, я думаю, что более или менее эквивалентным способом было бы вывести ее с помощью прямой ссылки и инициировать сравнение с T<A> вручную.

template<typename T>
class X;

template<template<typename> class T, typename A>
class X<T<A>> {
public:
using type = A;

template<typename _>
using template_ = T<_>;
};

template<typename T, typename R>
struct copyref {
using type = T;
};
template<typename T, typename R>
struct copyref<T, R&> {
using type = T&;
};
template<typename T, typename R>
struct copyref<T, R&&> {
using type = T&&;
};

template <typename U, typename XX = X<std::decay_t<U>>,
typename = typename XX::type >
decltype(auto) f (U && t)
{
return std::forward<
typename copyref<
typename XX::template template_<typename XX::type>, U
>::type>(t);
}

Если вы на самом деле не хотите T<A> но определенный тип, лучший способ заключается в использовании std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>>что гораздо проще, я думаю.

int main() {
static_assert(std::is_same<decltype(f(std::declval<X<int>&>())),
X<int>&>::value, "wrong");
static_assert(std::is_same<decltype(f(std::declval<X<int>>())),
X<int>&&>::value, "wrong");
// compile error
//f(std::declval<int>());
}

0

Недостаточно иметь вывод типа. Форма объявления типа должна быть именно так T&& (Rvalue ссылка на просто параметр шаблона). Если это не так (или нет вывода типа), параметр является ссылкой на rvalue. Если аргумент является lvalue, он не будет компилироваться. поскольку T<A>&& не имеет этой формы, f (T<A> && t) не может принять lvalue (как ссылку lvalue), и вы получаете ошибку. Если вы думаете, что это требует слишком много общего, считайте, что простой const классификатор тоже ломает:

template<typename T>
void f(const T&& param); // rvalue reference because of const

(оставляя в стороне относительную бесполезность ссылки на постоянное значение)

Правила для свертывания ссылок просто не действуют, если не использовать наиболее общую форму T&& используется. Без способности к f распознать именующий аргумент был передан и обрабатывать параметр как lvalue ссылка, нет ссылки на свертывание, которое должно быть сделано (т.е. свертывание T& && в T& не может случиться, и это просто T<something>&&, r refue ref. на шаблонный тип). Необходимый механизм для функции, чтобы определить, передается ли значение или значение в качестве аргумента, кодируется в выводимом параметре шаблона. Тем не менее, это кодирование происходит только для универсального опорного параметра, так как строго определены.

Почему этот уровень общности необходим (помимо того, что является правилом)? Без этого специфического формата определения универсальные ссылки не могли бы быть супержадными функциями, которые создают экземпляр любого типа аргумента … как они предназначены. Ответ Даниэля доходит до сути, я думаю: Предположим, вы хотите определить функцию с регулярной ссылкой на параметр шаблонного типа, T<A>&& (т.е. это не принимает аргумент lvalue). Если следующий синтаксис рассматривается как универсальная ссылка, то как бы вы изменили его, указав обычную ссылку на значение?

template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t) // rv-ref - how else would you exclude lvalue arguments?

Должен быть способ явного определения параметра как ссылки на rvalue, чтобы исключить аргументы lvalue. Это рассуждение, по-видимому, применимо к другим типам параметров, включая квалификации cv.

Кроме того, кажется, что есть способы обойти это (см. Черты и SFINAE), но я не могу ответить на эту часть. 🙂

0

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

В терминологии языка С++ Отдельно стоящая константа — это тоже выражение, хоть в нем и нет никаких «операций».

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

Выражение времени компиляции — это выражение, результат которого известен времени компиляции.

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Так о чем идет речь? Что мы «не можем»?

Речь конкретно об этом:

Цитата
Сообщение от DrOffset
Посмотреть сообщение

Следовательно мы можем взять адрес статической функции использовать его в выражениях времени компиляции.

и об этом:

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Взятие адреса функции (что с оператором ‘&’, что без) — выражение времени компиляции.

Это не верно.

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

Однако это не так, и пример с enum тому красноречивое подтверждение.

Вы конечно можете любую в принципе синтаксическую запись на зыке назвать «выражением времени компиляции».

Однако, не всякое выражение является выражением времени компиляции , и в этом смысле диалог не имеет смысла.

На практике смысл фразы «выражения времени компиляции» появляется тогда, и только тогда, когда мы, программисты, можем использовать их для своих целей: метапрограммирования, и компалтайм-алгоритмы.

Ну так вот, использовать адреса функция в компалтайм алгоритмах не получится.

Потому что в этих алгоритмах можно использовать только и только те выражения, результат которых известен времени компиляции.

Адрес функции времени компиляции не известен.
И поэтому, взятие адреса — есть выражение времени выполнения, а не компиляции.

Добавлено через 2 минуты

Цитата
Сообщение от DrOffset
Посмотреть сообщение

Как же он его не использует, когда он из них (из значений) формирует массив?

Я вам ещё раз повторю:
1. constexpr это просьба, а не приказ.
2. Вы используете имя объекта, а не значение адреса.

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

Поэтому я в третий раз повторюсь: не путаете понятие «выражение времени компиляции» и «тело шаблона».

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

Такие конструкции не являются выражением времени компиляции.

  

Rodinyr

17.08.11 — 01:46

Срздал шаблон ActiveDocument, он хранит вордовский шаблон.

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

кусок кода:

 Док=КомОбъект.Application.Documents(1);

        Заменить=док.Content.Find;

        Заменить.Execute(«Код»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.Код);

        Заменить.Execute(«Дата»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.Дата);

        Заменить.Execute(«ФИО»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.фио);

        Заменить.Execute(«Сумма»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.Сумма);

        Заменить.Execute(«СуммаПрописью»,Ложь,Истина,ЛОжь,,,Истина,Ложь,ЧислоПрописью(Выборка.Сумма, «Л = ru_RU; ДП = Истина», «рубль, рубля, рублей, м,копейка, копейки, копеек, ж,2») );

        Заменить.Execute(«Процент»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.Процент);

        Заменить.Execute(«Срок»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.Срок);

        Заменить.Execute(«ДатаВозврата»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.ДатаВозврата);

        Заменить.Execute(«СуммаПогашения»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Формат(Выборка.Сумма+Выборка.Сумма*Выборка.Процент*Выборка.Срок/100,»ЧЦ=10;ЧДЦ=2;ЧРД=.»));

        Заменить.Execute(«Штраф»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.Штраф);

        Заменить.Execute(«ПоднятыйПроцент»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.ПоднятыйПроцент);

    Заменить.Execute(«Заемщик»,Ложь,Истина,ЛОжь,,,Истина,Ложь,Выборка.фио+» «+Паспорт);

        док.SaveAs(ВременныйФайл);

При сборке возникает ошибка:

#include <iostream>
const int DefaultSize=10;
using namespace std;

template <class T>
class Array
{
  public:
    Array(int itsSize=DefaultSize);
    Array(const Array& rhs);
    ~Array() {delete [] pType;}

    Array& operator = (const Array&);
    T& operator[] (int offset){return pType[offset];}
    const T& operator [] (int offset) const {return pType[offset];}

    friend void Intrude(Array<int>);

    friend ostream& operator<< (ostream&, Array<T>&);  // < Компилятор указывает на 
    int GetSize() const {return itsSize;}              //эту строку

  private:
    T *pType;
    int itsSize;
};

template <class T>
ostream& operator << (ostream& output, Array<T>& theArray)
{
  for (int i=0;i<theArray.GetSize();i++)
    output<< "["<<i<<"]"<<theArray[i]<<endl;
  return output;
}

отчет во время компиляции:

g++ -Wall -c «template.cpp» (в каталоге: /host/projects/template)
template.cpp:30:52: warning: friend declaration ‘std::ostream& >operator<<(std::ostream&, Array<t>&)’ declares a non-template function
template.cpp:30:52: note: (if this is not what you intended, make sure the function >template has already been declared and add <> after the function name here)
Сборка прошла успешно.

отчет при сборке:

g++ -Wall -o «template» «template.cpp» (в каталоге: /host/projects/template)
template.cpp:30:52: warning: friend declaration ‘std::ostream& >operator<<(std::ostream&, Array<t>&)’ declares a non-template function
template.cpp:30:52: note: (if this is not what you intended, make sure the function >template has already been declared and add <> after the function name here)
/tmp/cciPebmn.o: In function main':
template.cpp:(.text+0x162): undefined reference to >
operator<<(std::basic_ostream<char, std::char_traits<char=»»> >&, Array<int>&)’
collect2: ld returned 1 exit status
Сборка завершилась с ошибкой.

Ну и собственно вопрос: в чем ошибка?

Harry's user avatar

Harry

210k15 золотых знаков114 серебряных знаков224 бронзовых знака

задан 23 мая 2011 в 20:49

Tomagavk's user avatar

template <typename T>
class Array
{
...
template <class X> friend ostream& operator<< (ostream& str, Array<X>& ar);
...
}

template <typename T>
ostream& operator << (ostream& output, Array<T>& theArray)
{
  for (int i=0;i<theArray.GetSize();i++)
    output<< "["<<i<<"]"<<theArray[i]<<endl;
  return output;
}

ответ дан 23 мая 2011 в 21:59

gecube's user avatar

gecubegecube

14k32 серебряных знака46 бронзовых знаков

0

Полагаю, в описании класса перед

friend ostream& operator<< (ostream&, Array<T>&);

надо поставить template<class T>:

template<class T>
friend ostream& operator<< (ostream&, Array<T>&);

friend это ведь не метод, это такая хитрая глобальная декларация. Соответственно, классовый шаблон на него не действует.

Отмечу, что friend-декларация тут в принципе лишняя, ведь operator<< обращается только к публичным членам.

Nicolas Chabanovsky's user avatar

ответ дан 24 мая 2011 в 6:26

Пётр Таран's user avatar

2

Вместо

friend ostream& operator<< (ostream&, Array<T>&);

напишите:

friend ostream& operator<< <T>(ostream&, Array<T>&);

На gcc не проверял, но должно помочь.

ответ дан 23 мая 2011 в 21:30

Fiztex's user avatar

FiztexFiztex

7423 серебряных знака9 бронзовых знаков

5

Сам оператор

template <class T>
ostream& operator << (ostream& output, Array<T>& theArray)

сделай инлайном:

template <class T> inline
ostream& operator << (ostream& output, Array<T>& theArray)

ответ дан 23 мая 2011 в 22:39

Ander's user avatar

AnderAnder

7314 серебряных знака10 бронзовых знаков

5

Вот код, все компилируется на gcc++ 4.4.3 и даже запускается. В чем вопрос?

~/tmp$ g++ --version
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

~/tmp$ g++ template.cpp
~/tmp$ ./a.out
[0]0
[1]0
[2]0
[3]0
[4]0
[5]0
[6]0
[7]0
[8]0
[9]0

template.cpp:

#include <iostream>
const int DefaultSize=10;
using namespace std;

template <class T>
class Array
{
  public:
    Array(int s=DefaultSize) : itsSize(s), pType( new T[s] ) {}
    Array(const Array& rhs);
    ~Array() {delete [] pType;}

    Array& operator = (const Array&);
    T& operator[] (int offset){return pType[offset];}
    const T& operator [] (int offset) const {return pType[offset];}

    friend void Intrude(Array<int>);

    template <class _T> friend ostream& operator<< (ostream&, Array<_T>&);  // < Компилятор указывает на

    int GetSize() const {return itsSize;}              //эту строку

  private:
    T *pType;
    int itsSize;
};

template <class T> inline
ostream& operator << (ostream& output, Array<T>& theArray)
{
  for (int i=0;i<theArray.GetSize();i++)
    output<< "["<<i<<"]"<<theArray[i]<<endl;
  return output;
}

int main()
{
    Array<double> xxx;
    cout << xxx << endl;
    return 0;
}

ответ дан 24 мая 2011 в 19:11

Ander's user avatar

AnderAnder

7314 серебряных знака10 бронзовых знаков

I am porting a relatively big code from Linux to Windows (Visual Studio 2008, I have to use that).
I completed the porting and it’s working, but I had to comment a small piece of code because Visual Studio gave error on that and I couldn’t exactly understand why.
So, basically, there is a functor that collects from a std::map all the «items» that satify a certain condition, defined in the following way:

/*
 * Map collector
 */
template<class TMap>
class CMapCollector
{
    public:
        typedef typename TMap::value_type tValueType;

        CMapCollector(void)
        { }

        void operator () (const tValueType& rcValue)
        {
            if (CheckCondition(rcValue))
                mCollector.push(rcValue);
        }

        bool NextResult(void) const
        {
            return (!mCollector.empty());
        }

        tValueType GetResult(void)
        {
            if (!NextResult())
                return tValueType();

            tValueType curr_value = mCollector.front();
            mCollector.pop();
            return curr_value;
        }

    private:
        virtual bool CheckCondition(const tValueType& rcValue) const = 0;

        typedef std::queue<tValueType> tCollectorContainer;

        tCollectorContainer mCollector;
};

Then, by inheriting from it, some collector classes are defined.
I wrote a small example that shows the error, in order to extract it from all the other code:

/*
 * Some class
 */
class CMyClass
{
    public:
        CMyClass(const int cNum) : mNum(cNum)
        { }

        bool DifferentFrom(const int cFrom) const
        {
            return (Get() != cFrom);
        }

        bool EqualTo(const int cTo) const
        {
            return (Get() == cTo);
        }

    private:
        int Get(void) const
        {
            return mNum;
        }

        int mNum;
};

/* Some map definition */
typedef std::map<int, CMyClass *> tMyMap;

/*
 * Real collectors
 */

class CNoNullCollector : public CMapCollector<tMyMap>
{
    private:
        bool CheckCondition(const tValueType& rcValue) const
        {
            return (rcValue.second->DifferentFrom(0));
        }
};

class CValueCollector : public CMapCollector<tMyMap>
{
    public:
        CValueCollector(const int cValue) : mValue(cValue)
        { }

    private:
        bool CheckCondition(const tValueType& rcValue) const
        {
            return (rcValue.second->EqualTo(mValue));
        }

        int mValue;
};

/*
 * main
 */
int main(int argc, char *argv[])
{
    tMyMap my_map;

    /* Insert some value */
    my_map.insert(std::make_pair(1, new CMyClass(0)));
    my_map.insert(std::make_pair(2, new CMyClass(1)));
    my_map.insert(std::make_pair(3, new CMyClass(2)));
    my_map.insert(std::make_pair(4, new CMyClass(2)));
    my_map.insert(std::make_pair(5, new CMyClass(3)));

    /* Collect values */
    CNoNullCollector collector = std::for_each(my_map.begin(), my_map.end(), CNoNullCollector());
    while (collector.NextResult())
    {
        CNoNullCollector::tValueType curr_result = collector.GetResult();
        /* Do something ... */
    }

    /* Free memory, not written ... */

    return 0;
}

This code compiles fine on Linux with g++ (I tried with both g++ 4.2 and 4.9, available on the machine).
I also tried with Visual Studio 2013, and it was fine.
However, compiling it with VS2008 (and also VS2010), it gives errors when it instantiates the class template «CMapCollector».
The errors are in the std::swap function, called from std::pair, at this point («utility» file, a standard include):

template<class _Ty> inline
void swap(_Ty& _Left, _Ty& _Right)
{   // exchange values stored at _Left and _Right
if (&_Left != &_Right)
    {   // different, worth swapping
    _Ty _Tmp = _Left;

    _Left = _Right;     // <-- error C3892: '_Left' : you cannot assign to a variable that is const
    _Right = _Tmp;      // <-- error C3892: '_Right' : you cannot assign to a variable that is const
    }
}

This is the complete error message:

    c:program files (x86)microsoft visual studio 9.0vcincludeutility(22) : error C3892: '_Left' : you cannot assign to a variable that is const
        c:program files (x86)microsoft visual studio 9.0vcincludeutility(31) : see reference to function template instantiation 'void std::swap<_Ty>(_Ty &,_Ty &)' being compiled
        with
        [
            _Ty=int
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludeutility(64) : see reference to function template instantiation 'void std::_Swap_adl<_Ty1>(_Ty &,_Ty &)' being compiled
        with
        [
            _Ty1=int,
            _Ty=int
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludeutility(61) : while compiling class template member function 'void std::pair<_Ty1,_Ty2>::swap(std::pair<_Ty1,_Ty2> &)'
        with
        [
            _Ty1=const int,
            _Ty2=CMyClass *
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludedeque(518) : see reference to class template instantiation 'std::pair<_Ty1,_Ty2>' being compiled
        with
        [
            _Ty1=const int,
            _Ty2=CMyClass *
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludequeue(24) : see reference to class template instantiation 'std::deque<_Ty>' being compiled
        with
        [
            _Ty=std::pair<const int,CMyClass *>
        ]
        c:usersmy_namedesktoptesttesttest.cpp(42) : see reference to class template instantiation 'std::queue<_Ty>' being compiled
        with
        [
            _Ty=std::pair<const int,CMyClass *>
        ]
        c:usersmy_namedesktoptesttesttest.cpp(81) : see reference to class template instantiation 'CMapCollector<TMap>' being compiled
        with
        [
            TMap=tMyMap
        ]
c:program files (x86)microsoft visual studio 9.0vcincludeutility(23) : error C3892: '_Right' : you cannot assign to a variable that is const

If I comment the part in which the class template is instantiated:

CNoNullCollector collector = std::for_each(my_map.begin(), my_map.end(), CNoNullCollector());
while (collector.NextResult())
{
    CNoNullCollector::tValueType curr_result = collector.GetResult();
    /* Do something ... */
}

the compilation will be successfully completed.
I understand there is some problem with the constantness, but I don’t understand where. Why does g++ successfully compile it?

EDIT:

I understood that it’s something related to the std::queue and the «key» of the std::map (and therefore the «first» of the std::pair) that is constant by definition, but I didn’t figure out how to solve it

I am porting a relatively big code from Linux to Windows (Visual Studio 2008, I have to use that).
I completed the porting and it’s working, but I had to comment a small piece of code because Visual Studio gave error on that and I couldn’t exactly understand why.
So, basically, there is a functor that collects from a std::map all the «items» that satify a certain condition, defined in the following way:

/*
 * Map collector
 */
template<class TMap>
class CMapCollector
{
    public:
        typedef typename TMap::value_type tValueType;

        CMapCollector(void)
        { }

        void operator () (const tValueType& rcValue)
        {
            if (CheckCondition(rcValue))
                mCollector.push(rcValue);
        }

        bool NextResult(void) const
        {
            return (!mCollector.empty());
        }

        tValueType GetResult(void)
        {
            if (!NextResult())
                return tValueType();

            tValueType curr_value = mCollector.front();
            mCollector.pop();
            return curr_value;
        }

    private:
        virtual bool CheckCondition(const tValueType& rcValue) const = 0;

        typedef std::queue<tValueType> tCollectorContainer;

        tCollectorContainer mCollector;
};

Then, by inheriting from it, some collector classes are defined.
I wrote a small example that shows the error, in order to extract it from all the other code:

/*
 * Some class
 */
class CMyClass
{
    public:
        CMyClass(const int cNum) : mNum(cNum)
        { }

        bool DifferentFrom(const int cFrom) const
        {
            return (Get() != cFrom);
        }

        bool EqualTo(const int cTo) const
        {
            return (Get() == cTo);
        }

    private:
        int Get(void) const
        {
            return mNum;
        }

        int mNum;
};

/* Some map definition */
typedef std::map<int, CMyClass *> tMyMap;

/*
 * Real collectors
 */

class CNoNullCollector : public CMapCollector<tMyMap>
{
    private:
        bool CheckCondition(const tValueType& rcValue) const
        {
            return (rcValue.second->DifferentFrom(0));
        }
};

class CValueCollector : public CMapCollector<tMyMap>
{
    public:
        CValueCollector(const int cValue) : mValue(cValue)
        { }

    private:
        bool CheckCondition(const tValueType& rcValue) const
        {
            return (rcValue.second->EqualTo(mValue));
        }

        int mValue;
};

/*
 * main
 */
int main(int argc, char *argv[])
{
    tMyMap my_map;

    /* Insert some value */
    my_map.insert(std::make_pair(1, new CMyClass(0)));
    my_map.insert(std::make_pair(2, new CMyClass(1)));
    my_map.insert(std::make_pair(3, new CMyClass(2)));
    my_map.insert(std::make_pair(4, new CMyClass(2)));
    my_map.insert(std::make_pair(5, new CMyClass(3)));

    /* Collect values */
    CNoNullCollector collector = std::for_each(my_map.begin(), my_map.end(), CNoNullCollector());
    while (collector.NextResult())
    {
        CNoNullCollector::tValueType curr_result = collector.GetResult();
        /* Do something ... */
    }

    /* Free memory, not written ... */

    return 0;
}

This code compiles fine on Linux with g++ (I tried with both g++ 4.2 and 4.9, available on the machine).
I also tried with Visual Studio 2013, and it was fine.
However, compiling it with VS2008 (and also VS2010), it gives errors when it instantiates the class template «CMapCollector».
The errors are in the std::swap function, called from std::pair, at this point («utility» file, a standard include):

template<class _Ty> inline
void swap(_Ty& _Left, _Ty& _Right)
{   // exchange values stored at _Left and _Right
if (&_Left != &_Right)
    {   // different, worth swapping
    _Ty _Tmp = _Left;

    _Left = _Right;     // <-- error C3892: '_Left' : you cannot assign to a variable that is const
    _Right = _Tmp;      // <-- error C3892: '_Right' : you cannot assign to a variable that is const
    }
}

This is the complete error message:

    c:program files (x86)microsoft visual studio 9.0vcincludeutility(22) : error C3892: '_Left' : you cannot assign to a variable that is const
        c:program files (x86)microsoft visual studio 9.0vcincludeutility(31) : see reference to function template instantiation 'void std::swap<_Ty>(_Ty &,_Ty &)' being compiled
        with
        [
            _Ty=int
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludeutility(64) : see reference to function template instantiation 'void std::_Swap_adl<_Ty1>(_Ty &,_Ty &)' being compiled
        with
        [
            _Ty1=int,
            _Ty=int
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludeutility(61) : while compiling class template member function 'void std::pair<_Ty1,_Ty2>::swap(std::pair<_Ty1,_Ty2> &)'
        with
        [
            _Ty1=const int,
            _Ty2=CMyClass *
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludedeque(518) : see reference to class template instantiation 'std::pair<_Ty1,_Ty2>' being compiled
        with
        [
            _Ty1=const int,
            _Ty2=CMyClass *
        ]
        c:program files (x86)microsoft visual studio 9.0vcincludequeue(24) : see reference to class template instantiation 'std::deque<_Ty>' being compiled
        with
        [
            _Ty=std::pair<const int,CMyClass *>
        ]
        c:usersmy_namedesktoptesttesttest.cpp(42) : see reference to class template instantiation 'std::queue<_Ty>' being compiled
        with
        [
            _Ty=std::pair<const int,CMyClass *>
        ]
        c:usersmy_namedesktoptesttesttest.cpp(81) : see reference to class template instantiation 'CMapCollector<TMap>' being compiled
        with
        [
            TMap=tMyMap
        ]
c:program files (x86)microsoft visual studio 9.0vcincludeutility(23) : error C3892: '_Right' : you cannot assign to a variable that is const

If I comment the part in which the class template is instantiated:

CNoNullCollector collector = std::for_each(my_map.begin(), my_map.end(), CNoNullCollector());
while (collector.NextResult())
{
    CNoNullCollector::tValueType curr_result = collector.GetResult();
    /* Do something ... */
}

the compilation will be successfully completed.
I understand there is some problem with the constantness, but I don’t understand where. Why does g++ successfully compile it?

EDIT:

I understood that it’s something related to the std::queue and the «key» of the std::map (and therefore the «first» of the std::pair) that is constant by definition, but I didn’t figure out how to solve it

Имеется кластер серверов виртуализации на базе Windows Server 2012. Также имеется сервер с установленной системой System Center Virtual Machine manager (SC VMM).

Мне необходимо было создать шаблон виртуальной машины на базе 64-битной Windows Pro 7. Сама виртуальная машина подготовлена с помощью утилиты sysprep.

Далее в ходе работы мастера создания шаблона из готовой ВМ я настраивал только страницу настройки свойств ОС: указан ключ продукта, пароль локального администратора, сведения о подключении машины к домену.

Создание шаблона завершилось ошибкой:

Ошибка (2931)
VMM не удалось выполнить запрос. Подключение к агенту VMM на сервере виртуализации (srv-sc.moscow.sovmortrans.com) прервано.
Unknown error (0x80338029)

Рекомендуемое действие
Убедитесь, что служба удаленного управления Windows (WS-Management) и агент VMM установлены и запущены, а брандмауэр не блокирует трафик HTTPS.

Эта проблема также может быть связана с неполадками DNS. Убедитесь, что сервер (srv-sc.moscow.sovmortrans.com) доступен по сети и его можно найти в системе DNS. Можно выполнить проверку связи с сервером виртуализации с
сервера управления VMM и убедиться в том, что возвращенный IP-адрес соответствует IP-адресу, полученному от сервера виртуализации локально.

Если эта ошибка будет повторяться, перезагрузите сервер виртуализации и повторите операцию.

Поскольку виртуальная машина удаляется с хоста, повторить операцию не удается. Указанные службы работают. Исходящий трафик HTTPS с сервера виртуализации не блокируется. Нужно ли разрешать входящий трафик HTTPS я не нашел в документации.

Сбоем завершился шаг:

1.2.5. Развертывание файла (по протоколу BITS поверх HTTPS): 1 из 2 файлов (19,01 ГБ.20,66 ГБ)

Встречался ли кто-нибудь с такой проблемой и как ее разрешить?

Я нашел эту реализацию класса векторного шаблона, но она не компилируется в XCode.

Заголовочный файл:

// File: myvector.h

#ifndef _myvector_h
#define _myvector_h

template <typename ElemType>
class MyVector
{
public:
    MyVector();
~MyVector();
int size();
void add(ElemType s);
ElemType getAt(int index);

private:
ElemType *arr;
int numUsed, numAllocated;
void doubleCapacity();
};

#include "myvector.cpp"

#endif

Файл реализации:

// File: myvector.cpp

#include <iostream>
#include "myvector.h"

template <typename ElemType>
MyVector<ElemType>::MyVector()
{   
arr = new ElemType[2];
numAllocated = 2;
numUsed = 0;
}

template <typename ElemType>
MyVector<ElemType>::~MyVector()
{
delete[] arr;
}

template <typename ElemType>
int MyVector<ElemType>::size()
{
return numUsed;
}

template <typename ElemType>
ElemType MyVector<ElemType>::getAt(int index)
{
if (index < 0 || index >= size()) {
    std::cerr << "Out of Bounds";
    abort();
}
return arr[index];
}

template <typename ElemType>
void MyVector<ElemType>::add(ElemType s)
{
if (numUsed == numAllocated)
    doubleCapacity();
arr[numUsed++] = s;
}

template <typename ElemType>
void MyVector<ElemType>::doubleCapacity()
{
ElemType *bigger = new ElemType[numAllocated*2];
for (int i = 0; i < numUsed; i++)
    bigger[i] = arr[i];
delete[] arr;
arr = bigger;
numAllocated*= 2;
}

Если я попытаюсь скомпилировать как есть, я получаю следующую ошибку: «Переопределение MyVector :: MyVector ()». Такая же ошибка отображается для каждой функции-члена (файл .cpp).

Чтобы исправить это, я удалил ‘#include «myvector.h»’ из файла .cpp, но теперь я получаю новую ошибку: «Ожидаемый конструктор, деструктор или преобразование типа до токена ‘<‘». Аналогичная ошибка отображается и для каждого участника.

Интересно, что если я перенесу весь код .cpp в файл заголовка, он компилируется нормально. Означает ли это, что я не могу реализовать классы шаблонов в отдельных файлах?

2 ответа

Лучший ответ

Всегда рекомендуется размещать шаблоны в файле заголовка. Таким образом, вы не испортите компоновщик множеством определений одних и тех же экземпляров и тому подобного.

Ну и конечно круговое включение :).


0

Pieter
6 Июн 2010 в 19:30

Во-первых, у вас есть

 #include "myvector.cpp"

Который создает круговую ссылку между файлами. Просто избавься от этого.

Другая проблема заключается в том, что вы определяете класс шаблона внутри файла .cpp. Определения шаблонов разрешены только внутри файлов заголовков. Могут быть способы обойти это, но для g ++ (который использует XCode) именно так рассыпается cookie.

Traceback:
File "***/python2.7/site-packages/django/core/handlers/base.py" in get_response
  111.                     response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "***/python2.7/site-packages/django/contrib/admin/options.py" in wrapper
  583.                 return self.admin_site.admin_view(view)(*args, **kwargs)
File "***/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  105.                     response = view_func(request, *args, **kwargs)
File "***/python2.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  52.         response = view_func(request, *args, **kwargs)
File "***/python2.7/site-packages/django/contrib/admin/sites.py" in inner
  206.             return view(request, *args, **kwargs)
File "***/python2.7/site-packages/django/contrib/admin/options.py" in add_view
  1453.         return self.changeform_view(request, None, form_url, extra_context)
File "***/python2.7/site-packages/django/utils/decorators.py" in _wrapper
  29.             return bound_func(*args, **kwargs)
File "***/python2.7/site-packages/django/utils/decorators.py" in _wrapped_view
  105.                     response = view_func(request, *args, **kwargs)
File "***/python2.7/site-packages/django/utils/decorators.py" in bound_func
  25.                 return func.__get__(self, type(self))(*args2, **kwargs2)
File "***/python2.7/site-packages/django/db/transaction.py" in inner
  394.                 return func(*args, **kwargs)
File "***/python2.7/site-packages/django/contrib/admin/options.py" in changeform_view
  1427.             model_admin=self)
File "***/python2.7/site-packages/django/contrib/admin/helpers.py" in __init__
  36.         } for field_name, dependencies in prepopulated_fields.items()]
File "***/python2.7/site-packages/django/forms/forms.py" in __getitem__
  147.                 "Key %r not found in '%s'" % (name, self.__class__.__name__))

Exception Type: KeyError at /admin/dbmail/mailtemplate/add/
Exception Value: u"Key 'slug' not found in 'MailTemplateForm'"

При сборке возникает ошибка:

#include <iostream>
const int DefaultSize=10;
using namespace std;

template <class T>
class Array
{
  public:
    Array(int itsSize=DefaultSize);
    Array(const Array& rhs);
    ~Array() {delete [] pType;}

    Array& operator = (const Array&);
    T& operator[] (int offset){return pType[offset];}
    const T& operator [] (int offset) const {return pType[offset];}

    friend void Intrude(Array<int>);

    friend ostream& operator<< (ostream&, Array<T>&);  // < Компилятор указывает на 
    int GetSize() const {return itsSize;}              //эту строку

  private:
    T *pType;
    int itsSize;
};

template <class T>
ostream& operator << (ostream& output, Array<T>& theArray)
{
  for (int i=0;i<theArray.GetSize();i++)
    output<< "["<<i<<"]"<<theArray[i]<<endl;
  return output;
}

отчет во время компиляции:

g++ -Wall -c «template.cpp» (в каталоге: /host/projects/template)
template.cpp:30:52: warning: friend declaration ‘std::ostream& >operator<<(std::ostream&, Array<t>&)’ declares a non-template function
template.cpp:30:52: note: (if this is not what you intended, make sure the function >template has already been declared and add <> after the function name here)
Сборка прошла успешно.

отчет при сборке:

g++ -Wall -o «template» «template.cpp» (в каталоге: /host/projects/template)
template.cpp:30:52: warning: friend declaration ‘std::ostream& >operator<<(std::ostream&, Array<t>&)’ declares a non-template function
template.cpp:30:52: note: (if this is not what you intended, make sure the function >template has already been declared and add <> after the function name here)
/tmp/cciPebmn.o: In function main':
template.cpp:(.text+0x162): undefined reference to >
operator<<(std::basic_ostream<char, std::char_traits<char=»»> >&, Array<int>&)’
collect2: ld returned 1 exit status
Сборка завершилась с ошибкой.

Ну и собственно вопрос: в чем ошибка?

Harry's user avatar

Harry

215k15 золотых знаков117 серебряных знаков228 бронзовых знаков

задан 23 мая 2011 в 20:49

Tomagavk's user avatar

template <typename T>
class Array
{
...
template <class X> friend ostream& operator<< (ostream& str, Array<X>& ar);
...
}

template <typename T>
ostream& operator << (ostream& output, Array<T>& theArray)
{
  for (int i=0;i<theArray.GetSize();i++)
    output<< "["<<i<<"]"<<theArray[i]<<endl;
  return output;
}

ответ дан 23 мая 2011 в 21:59

gecube's user avatar

gecubegecube

14k33 серебряных знака46 бронзовых знаков

0

Полагаю, в описании класса перед

friend ostream& operator<< (ostream&, Array<T>&);

надо поставить template<class T>:

template<class T>
friend ostream& operator<< (ostream&, Array<T>&);

friend это ведь не метод, это такая хитрая глобальная декларация. Соответственно, классовый шаблон на него не действует.

Отмечу, что friend-декларация тут в принципе лишняя, ведь operator<< обращается только к публичным членам.

Nicolas Chabanovsky's user avatar

ответ дан 24 мая 2011 в 6:26

Пётр Таран's user avatar

2

Вместо

friend ostream& operator<< (ostream&, Array<T>&);

напишите:

friend ostream& operator<< <T>(ostream&, Array<T>&);

На gcc не проверял, но должно помочь.

ответ дан 23 мая 2011 в 21:30

Fiztex's user avatar

FiztexFiztex

7423 серебряных знака9 бронзовых знаков

5

Сам оператор

template <class T>
ostream& operator << (ostream& output, Array<T>& theArray)

сделай инлайном:

template <class T> inline
ostream& operator << (ostream& output, Array<T>& theArray)

ответ дан 23 мая 2011 в 22:39

Ander's user avatar

AnderAnder

7414 серебряных знака10 бронзовых знаков

5

Вот код, все компилируется на gcc++ 4.4.3 и даже запускается. В чем вопрос?

~/tmp$ g++ --version
g++ (Ubuntu 4.4.3-4ubuntu5) 4.4.3
Copyright (C) 2009 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

~/tmp$ g++ template.cpp
~/tmp$ ./a.out
[0]0
[1]0
[2]0
[3]0
[4]0
[5]0
[6]0
[7]0
[8]0
[9]0

template.cpp:

#include <iostream>
const int DefaultSize=10;
using namespace std;

template <class T>
class Array
{
  public:
    Array(int s=DefaultSize) : itsSize(s), pType( new T[s] ) {}
    Array(const Array& rhs);
    ~Array() {delete [] pType;}

    Array& operator = (const Array&);
    T& operator[] (int offset){return pType[offset];}
    const T& operator [] (int offset) const {return pType[offset];}

    friend void Intrude(Array<int>);

    template <class _T> friend ostream& operator<< (ostream&, Array<_T>&);  // < Компилятор указывает на

    int GetSize() const {return itsSize;}              //эту строку

  private:
    T *pType;
    int itsSize;
};

template <class T> inline
ostream& operator << (ostream& output, Array<T>& theArray)
{
  for (int i=0;i<theArray.GetSize();i++)
    output<< "["<<i<<"]"<<theArray[i]<<endl;
  return output;
}

int main()
{
    Array<double> xxx;
    cout << xxx << endl;
    return 0;
}

ответ дан 24 мая 2011 в 19:11

Ander's user avatar

AnderAnder

7414 серебряных знака10 бронзовых знаков

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

В терминологии языка С++ Отдельно стоящая константа — это тоже выражение, хоть в нем и нет никаких «операций».

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

Выражение времени компиляции — это выражение, результат которого известен времени компиляции.

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Так о чем идет речь? Что мы «не можем»?

Речь конкретно об этом:

Цитата
Сообщение от DrOffset
Посмотреть сообщение

Следовательно мы можем взять адрес статической функции использовать его в выражениях времени компиляции.

и об этом:

Цитата
Сообщение от TheCalligrapher
Посмотреть сообщение

Взятие адреса функции (что с оператором ‘&’, что без) — выражение времени компиляции.

Это не верно.

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

Однако это не так, и пример с enum тому красноречивое подтверждение.

Вы конечно можете любую в принципе синтаксическую запись на зыке назвать «выражением времени компиляции».

Однако, не всякое выражение является выражением времени компиляции , и в этом смысле диалог не имеет смысла.

На практике смысл фразы «выражения времени компиляции» появляется тогда, и только тогда, когда мы, программисты, можем использовать их для своих целей: метапрограммирования, и компалтайм-алгоритмы.

Ну так вот, использовать адреса функция в компалтайм алгоритмах не получится.

Потому что в этих алгоритмах можно использовать только и только те выражения, результат которых известен времени компиляции.

Адрес функции времени компиляции не известен.
И поэтому, взятие адреса — есть выражение времени выполнения, а не компиляции.

Добавлено через 2 минуты

Цитата
Сообщение от DrOffset
Посмотреть сообщение

Как же он его не использует, когда он из них (из значений) формирует массив?

Я вам ещё раз повторю:
1. constexpr это просьба, а не приказ.
2. Вы используете имя объекта, а не значение адреса.

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

Поэтому я в третий раз повторюсь: не путаете понятие «выражение времени компиляции» и «тело шаблона».

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

Такие конструкции не являются выражением времени компиляции.

Универсальные ссылки (то есть «прямые ссылки», c++ стандартное название) и идеальная пересылка в c++11, c++14и за ее пределами имеют много важных преимуществ; увидеть Вот, а также Вот.

В статье Скотта Мейерса, упомянутой выше (ссылка на сайт), как правило, утверждается, что:

Если переменная или параметр объявлены как имеющие тип T&& для некоторых выведенный тип T, эта переменная или параметр является универсальной ссылкой.

Пример 1

Действительно, используя clang ++, мы видим, что следующий фрагмент кода успешно скомпилируется с -std=c++14:

#include <utility>

template <typename T>
decltype(auto) f(T && t)
{
return std::forward<T>(t);
}

int        x1 = 1;
int const  x2 = 1;
int&       x3 = x1;
int const& x4 = x2;

// all calls to `f` result in a successful
// binding of T&& to the required types
auto r1 = f (x1);    // various lvalues okay, as expected
auto r2 = f (x2);    // ...
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (int()); // rvalues okay, as expected

Дано любое описание универсальных ссылок (прямые ссылки) и вывода типа (см., Например, это объяснение) понятно, почему вышесказанное работает. Хотя из того же объяснения не совсем понятно, почему нижеприведенное также не работает.

(не удалось) Пример 2

Этот вопрос решает ту же проблему. Однако предоставленные ответы не объясняют, почему шаблонные типы не классифицируются как «выводимые».

То, что я собираюсь показать (по-видимому), удовлетворяет требованиям, изложенным выше Мейерсом. Тем не менее, следующий код снят терпит неудачу компилировать, выдавая ошибку (среди прочего для каждого вызова f):

test.cpp: 23: 11: ошибка: нет подходящей функции для вызова ‘f’

auto r1 = f (x1);

test.cpp: 5: 16: примечание: функция-кандидат [с T = foo, A = int] not
жизнеспособный: нет известного преобразования из ‘struct foo< int> ‘to’ foo< int> &&’
за 1-й аргумент

decltype (авто) f (T)< A> && т)

#include <utility>

//
// It **seems** that the templated type T<A> should
// behave the same as an bare type T with respect to
// universal references, but this is not the case.
//
template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t)
{
return std::forward<T<A>> (t);
}

template <typename A>
struct foo
{
A bar;
};

struct foo<int>        x1 { .bar = 1 };
struct foo<int> const  x2 { .bar = 1 };
struct foo<int> &      x3 = x1;
struct foo<int> const& x4 = x2;

// all calls to `f` **fail** to compile due
// to **unsuccessful** binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);
auto r5 = f (foo<int> {1}); // only rvalue works

В контексте, так как тип T<A> из fпараметр является вывел, обязательно объявление параметра T<A>&& t будет вести себя как универсальная ссылка (прямая ссылка).

Пример 3 (для ясности в описании проблемы под рукой)

Позвольте мне подчеркнуть следующее: сбой кода в Example 2 компилировать это не благодаря тому факту, что struct foo<> это шаблонный тип. Похоже, причина неудачи только объявлением fПараметр как шаблонный тип.

Рассмотрим следующую редакцию предыдущего кода, которая сейчас делает компиляции:

#include <utility>

//
// If we re-declare `f` as before, where `T` is no longer a
// templated type parameter, our code works once more.
//
template <typename T>
decltype(auto) f (T && t)
{
return std::forward<T> (t);
}

//
// Notice, `struct foo<>` is **still** a templated type.
//
template <typename A>
struct foo
{
A bar;
};

struct foo<int>        x1 { .bar = 1 };
struct foo<int> const  x2 { .bar = 1 };
struct foo<int> &      x3 = x1;
struct foo<int> const& x4 = x2;

// all calls to `f` (again) result in
// a successful binding of T&& to the required types
auto r1 = f (x1);
auto r2 = f (x2);
auto r3 = f (x3);
auto r4 = f (x4);

Меня удивляет, что это простое изменение полностью меняет поведение вывода типа для шаблонной функции fТип параметра.

Вопросы:

Почему второй пример не работает, как ожидалось? Существуют ли способы преодоления этой проблемы с помощью шаблонных типов в c++11/14? Существуют ли хорошо известные существующие кодовые базы (в дикой природе), успешно использующие c++Прямые ссылки с шаблонными типами?

11

Решение

Когда вы вызываете какую-то функцию f с некоторым значением:

int a = 42;
f(a);

затем f должен быть в состоянии принять такое значение. Это тот случай, когда первый параметр f является (lvalue) ссылочным типом, или когда это вообще не ссылка:

auto f(int &);
auto f(int); // assuming a working copy constructor

это не будет работать, когда параметр является rvalue ссылкой:

auto f(int &&); // error

Теперь, когда вы определяете функцию со ссылкой для пересылки в качестве первого параметра, как вы делали в первом и третьем примере …

template<typename T>
auto f(T&&); // Showing only declaration

… и вы на самом деле вызываете эту функцию с lvalue, вычет типа шаблона превращается T в (lvalue) ссылку (то, что это происходит, можно увидеть в примере кода, который я приведу чуть позже):

auto f(int & &&); // Think of it like that

Конечно, здесь слишком много ссылок. Так что C ++ имеет рушатся правила, которые на самом деле довольно просты:

  • T& & становится T&
  • T& && становится T&
  • T&& & становится T&
  • T&& && становится T&&

Благодаря второму правилу, «эффективный» тип первого параметра f является ссылкой на lvalue, так что вы можете привязать ее к lvalue.

Теперь, когда вы определяете функцию g лайк …

template<template<class> class T, typename A>
auto g(T<A>&&);

Тогда несмотря ни на что, вычет параметра шаблона должен повернуть T в шаблон, не тип. Ведь именно это вы указали при объявлении параметра шаблона как template<class> class вместо typename,
(Это важное отличие, foo в вашем примере это не тип, это шаблон … который вы можете видеть как функцию уровня типа, но вернемся к теме)

Сейчас, T это какой-то шаблон. У вас не может быть ссылки на шаблон.
Ссылка (тип) создается из (возможно, неполного) тип. Так что ни на что, T<A> (который является типом, но нет параметр шаблона, который может быть выведен) не превратится в (lvalue) ссылку, что означает T<A> && не нуждается в свертывании и остается тем, что есть: ссылка на значение. И, конечно, вы не можете привязать lvalue к ссылке на rvalue.

Но если вы передадите ему значение, то даже g буду работать.

Все вышеперечисленное можно увидеть в следующем примере:

template<typename X>
struct thing {
};
template<typename T>
decltype (auto) f(T&& t) {
if (std::is_same<typename std::remove_reference<T>::type, T>::value) {
cout << "not ";
}
cout << "a reference" << endl;
return std::forward<T>(t);
}
template<
template<class> class T,
typename A>
decltype (auto) g(T<A>&& t) {
return std::forward<T<A>>(t);
}
int main(int, char**) {
thing<int> it {};

f(thing<int> {}); // "not a reference"
f(it);            // "a reference"// T = thing<int> &
// T&& = thing<int>& && = thing<int>&

g(thing<int> {}); // works

//g(it);
// T = thing
// A = int
// T<A>&& = thing<int>&&

return 0;
}

(Живи здесь)

Относительно того, как можно «преодолеть» это: вы не можете. По крайней мере, не так, как вы этого хотите, потому что естественное решение — третий пример, который вы предоставляете: поскольку вы не знаете переданный тип (это ссылка lvalue, ссылка rvalue или ссылка вообще?), вы должны сохранять его как общий T, Вы, конечно, могли бы обеспечить перегрузки, но это как-то не позволило бы достичь идеальной пересылки, я думаю.


Хм, оказывается, ты на самом деле Можно преодолеть это, используя некоторые черты класса:

template<typename> struct traits {};
template<
template<class>class T,
typename A>
struct traits<T<A>> {
using param = A;
template<typename X>
using templ = T<X>;
};

Затем вы можете извлечь как шаблон, так и тип, с которым был создан экземпляр шаблона внутри функции:

template<typename Y>
decltype (auto) g(Y&& t) {
// Needs some manual work, but well ...
using trait = traits<typename std::remove_reference<Y>::type>;
using A = typename trait::param;
using T = trait::template templ
// using it
T<A> copy{t};
A data;
return std::forward<Y>(t);
}

(Живи здесь)


[…] Можно вы объясните, почему это не универсальная ссылка? Какова будет опасность или подводный камень или ее слишком сложно реализовать? Я искренне заинтересован.

T<A>&& не универсальная ссылка, потому что T<A> не является параметром шаблона. Это (после вычета обоих T а также Aпростой (фиксированный / не универсальный) тип.

Серьезным подводным камнем для ссылки на пересылку было бы то, что вы больше не можете выразить текущее значение T<A>&&: Rvalue ссылка на некоторый тип, созданный из шаблона T с параметром A,

7

Другие решения

Почему второй пример не работает, как ожидалось?

У вас есть две подписи:

template <typename T>
decltype(auto) f (T&& );

template <template <typename> typename T, typename A>
decltype(auto) f2 (T<A>&& );

f принимает ссылку на пересылку, но f2 не. Конкретное правило, из [temp.deduct.call], выделено жирным шрифтом:

экспедиционная ссылка это значение
ссылка на резюме параметр шаблона. Если P является ссылкой для пересылки, а аргумент является
lvalue, тип «lvalue ссылка на A» используется вместо A для вывода типа.

С f, аргумент является ссылкой на параметр шаблонаT). Но с f2, T<A> не является параметром шаблона. Таким образом, эта функция просто принимает в качестве аргумента ссылку на значение T<A>, Вызовы не компилируются, потому что все ваши аргументы являются lvalue, и в этом случае нет особых исключений для вызова с lvalue.

2

Что касается преодоления проблемы, я думаю, что более или менее эквивалентным способом было бы вывести ее с помощью прямой ссылки и инициировать сравнение с T<A> вручную.

template<typename T>
class X;

template<template<typename> class T, typename A>
class X<T<A>> {
public:
using type = A;

template<typename _>
using template_ = T<_>;
};

template<typename T, typename R>
struct copyref {
using type = T;
};
template<typename T, typename R>
struct copyref<T, R&> {
using type = T&;
};
template<typename T, typename R>
struct copyref<T, R&&> {
using type = T&&;
};

template <typename U, typename XX = X<std::decay_t<U>>,
typename = typename XX::type >
decltype(auto) f (U && t)
{
return std::forward<
typename copyref<
typename XX::template template_<typename XX::type>, U
>::type>(t);
}

Если вы на самом деле не хотите T<A> но определенный тип, лучший способ заключается в использовании std::enable_if_t<std::is_same_v<std::decay_t<U>, SpecificType>>что гораздо проще, я думаю.

int main() {
static_assert(std::is_same<decltype(f(std::declval<X<int>&>())),
X<int>&>::value, "wrong");
static_assert(std::is_same<decltype(f(std::declval<X<int>>())),
X<int>&&>::value, "wrong");
// compile error
//f(std::declval<int>());
}

0

Недостаточно иметь вывод типа. Форма объявления типа должна быть именно так T&& (Rvalue ссылка на просто параметр шаблона). Если это не так (или нет вывода типа), параметр является ссылкой на rvalue. Если аргумент является lvalue, он не будет компилироваться. поскольку T<A>&& не имеет этой формы, f (T<A> && t) не может принять lvalue (как ссылку lvalue), и вы получаете ошибку. Если вы думаете, что это требует слишком много общего, считайте, что простой const классификатор тоже ломает:

template<typename T>
void f(const T&& param); // rvalue reference because of const

(оставляя в стороне относительную бесполезность ссылки на постоянное значение)

Правила для свертывания ссылок просто не действуют, если не использовать наиболее общую форму T&& используется. Без способности к f распознать именующий аргумент был передан и обрабатывать параметр как lvalue ссылка, нет ссылки на свертывание, которое должно быть сделано (т.е. свертывание T& && в T& не может случиться, и это просто T<something>&&, r refue ref. на шаблонный тип). Необходимый механизм для функции, чтобы определить, передается ли значение или значение в качестве аргумента, кодируется в выводимом параметре шаблона. Тем не менее, это кодирование происходит только для универсального опорного параметра, так как строго определены.

Почему этот уровень общности необходим (помимо того, что является правилом)? Без этого специфического формата определения универсальные ссылки не могли бы быть супержадными функциями, которые создают экземпляр любого типа аргумента … как они предназначены. Ответ Даниэля доходит до сути, я думаю: Предположим, вы хотите определить функцию с регулярной ссылкой на параметр шаблонного типа, T<A>&& (т.е. это не принимает аргумент lvalue). Если следующий синтаксис рассматривается как универсальная ссылка, то как бы вы изменили его, указав обычную ссылку на значение?

template <template <typename> typename T, typename A>
decltype(auto) f (T<A> && t) // rv-ref - how else would you exclude lvalue arguments?

Должен быть способ явного определения параметра как ссылки на rvalue, чтобы исключить аргументы lvalue. Это рассуждение, по-видимому, применимо к другим типам параметров, включая квалификации cv.

Кроме того, кажется, что есть способы обойти это (см. Черты и SFINAE), но я не могу ответить на эту часть. 🙂

0

  • Ошибка при создании формы см exception innerexception
  • Ошибка при создании фейсбука
  • Ошибка при создании фаски ребра компас
  • Ошибка при создании файла проекта reaper
  • Ошибка при создании учетной записи windows 10