Как правильно предоставить доказательства конструктору класса case, который зависит от класса аргумента

Способ иметь отдельную реализацию выглядит следующим образом.

//inner_foo.h

template <typename T>
struct Foo
{
    void doSomething(T param);
};


//foo.tpp
#include "inner_foo.h"
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}


//foo.h
#include <foo.tpp>

//main.cpp
#include <foo.h>

inner_foo имеет форвардные объявления. foo.tpp имеет реализацию и включает inner_foo.h; и foo.h будет иметь только одну строку, чтобы включить foo.tpp.

Во время компиляции содержимое foo.h копируется в foo.tpp, а затем весь файл копируется в foo.h после который он компилирует. Таким образом, ограничений нет, и именование согласовано в обмен на один дополнительный файл.

Я делаю это, потому что статические анализаторы для кода разбиваются, когда он не видит передовые объявления класса в * .tpp. Это раздражает при написании кода в любой среде IDE или с помощью YouCompleteMe или других.

0
задан Dmytro Mitin 16 January 2019 в 20:19
поделиться

1 ответ

Попробуйте параметризовать классы дел:

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  case class C[S <: State](state: S)(implicit ev: (A |∨| B)#λ[S])

  val a = A()
  val b = B()
  val n = N()

  val ca = C(a)
  val cb = C(b)
  // val cn = C(n) // doesn't compile

Кодирование для большего количества классов

  type ![S] = S => Nothing
  type !![S] = ![![S]]

  trait Disj[T] {
    type or[S] = Disj[T with ![S]]
    type apply = ![T]
  }

  // for convenience
  type disj[T] = { type or[S] = Disj[![T]]#or[S] }

  type w[T, U, V] = disj[T]#or[U]#or[V]#apply
  type ww[T, U, V] = {type λ[X] = !![X] <:< w[T, U, V]}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class C() extends State
  case class N() extends State

  case class D[S <: State](state: S)(implicit ev: ww[A, B, C]#λ[S])

  val a = A()
  val b = B()
  val c = C()
  val n = N()

  val da = D(a)
  val db = D(b)
  val dc = D(c)
//  val dn = D(n) // doesn't compile

Или вы можете использовать метод apply

[112 ]

Если вы хотите избежать сопутствующих объектов с помощью метода apply (по некоторым причинам), вы можете рассмотреть вторичные конструкторы

  type ![S] = S => Nothing
  type !![S] = ![![S]]
  type ∨[T, U] = ![![T] with ![U]]
  type |∨|[T, U] = {type λ[X] = !![X] <:< (T ∨ U)}

  abstract class State
  case class A() extends State
  case class B() extends State
  case class N() extends State

  // "ignored" is to avoid constructor ambiguity
  class C private(state: State, ignored: Int) {
    def this(state: State)(implicit ev: (A |∨| B)#λ[state.type]) = this(state, 0)
  }

  val a = A()
  val b = B()
  val n = N()

  val ca = new C(a)
  val cb = new C(b)
//  val cn = new C(n) // doesn't compile

Используя вторичный конструктор или объект сопутствующего объекта apply Метод является стандартным обходным путем для обработки такого рода зависимостей в конструкторах ( 1 2 3 ). Альтернатива состоит в том, чтобы ждать реальных типов объединений в Dotty (текущий подход - их частичная эмуляция) или разрешать SI-5712 в Scala.

Для автоматической генерации сопутствующих объектов вы можете рассмотреть макросы или генерацию кода.

0
ответ дан Dmytro Mitin 16 January 2019 в 20:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: